Merge "Adds automatic switching to Guest if user starts driving with the keyguard up."
diff --git a/Android.bp b/Android.bp
index f9b60e6..d94bd84 100644
--- a/Android.bp
+++ b/Android.bp
@@ -560,6 +560,7 @@
         "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
         "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
         "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
+        "telephony/java/com/android/internal/telephony/IRcs.aidl",
         "telephony/java/com/android/internal/telephony/ISms.aidl",
         "telephony/java/com/android/internal/telephony/ISub.aidl",
         "telephony/java/com/android/internal/telephony/IAns.aidl",
@@ -1220,11 +1221,6 @@
     srcs_lib_whitelist_dirs: frameworks_base_subdirs,
     srcs_lib_whitelist_pkgs: packages_to_document,
     libs: [
-        "core-oj",
-        "core-libart",
-        "conscrypt",
-        "bouncycastle",
-        "okhttp",
         "ext",
         "framework",
         "voip-common",
diff --git a/Android.mk b/Android.mk
index d333074..770ec20 100644
--- a/Android.mk
+++ b/Android.mk
@@ -87,6 +87,7 @@
     frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
     frameworks/base/config/hiddenapi-light-greylist.txt \
     frameworks/base/config/hiddenapi-vendor-list.txt \
+    frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
     frameworks/base/config/hiddenapi-force-blacklist.txt \
     $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
     $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
@@ -98,6 +99,7 @@
 	    --input-greylists \
 	        frameworks/base/config/hiddenapi-light-greylist.txt \
 	        frameworks/base/config/hiddenapi-vendor-list.txt \
+	        frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
 	        <(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \
 	                   $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
 	        $(PRIVATE_GREYLIST_INPUTS) \
@@ -111,6 +113,17 @@
 	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
 	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
 
+$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
+    frameworks/base/tools/hiddenapi/merge_csv.py \
+    $(PRIVATE_METADATA_INPUTS)
+	frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
+
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
+
 # Include subdirectory makefiles
 # ============================================================
 
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ff40f75..5c21221 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,6 +5,7 @@
                       packages/PrintRecommendationService/
                       packages/PrintSpooler/
                       packages/PackageInstaller/
+                      packages/SystemUI/
                       services/print/
                       services/usb/
                       telephony/
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index eed1db0..c742df3 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -17,14 +17,14 @@
 package android.graphics.perftests;
 
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.Bitmap.Config;
+import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 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;
@@ -43,7 +43,7 @@
         RenderNode child = RenderNode.create("child", null);
         child.setLeftTopRightBottom(50, 50, 100, 100);
 
-        DisplayListCanvas canvas = node.start(100, 100);
+        RecordingCanvas canvas = node.start(100, 100);
         node.end(canvas);
         canvas = child.start(50, 50);
         canvas.drawColor(Color.WHITE);
@@ -70,7 +70,7 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode node = RenderNode.create("benchmark", null);
 
-        DisplayListCanvas canvas = node.start(100, 100);
+        RecordingCanvas canvas = node.start(100, 100);
         node.end(canvas);
         Bitmap bitmap = Bitmap.createBitmap(80, 80, Config.ARGB_8888);
         Paint paint = new Paint();
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
index 3a4fc72..f9c3758 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
@@ -20,7 +20,6 @@
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.support.test.filters.LargeTest;
-import android.view.RenderNode;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index a283e06..d18aa51 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -17,17 +17,15 @@
 package android.graphics.perftests;
 
 import android.graphics.Outline;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 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 java.util.ArrayList;
-
 @LargeTest
 public class RenderNodePerfTest {
     @Rule
@@ -73,7 +71,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode node = RenderNode.create("LinearLayout", null);
         while (state.keepRunning()) {
-            DisplayListCanvas canvas = node.start(100, 100);
+            RecordingCanvas canvas = node.start(100, 100);
             node.end(canvas);
         }
     }
@@ -82,7 +80,7 @@
     public void testStartEndDeepHierarchy() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         RenderNode[] nodes = new RenderNode[30];
-        DisplayListCanvas[] canvases = new DisplayListCanvas[nodes.length];
+        RecordingCanvas[] canvases = new RecordingCanvas[nodes.length];
         for (int i = 0; i < nodes.length; i++) {
             nodes[i] = RenderNode.create("LinearLayout", null);
         }
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
index 64f2800..9245c1b 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -18,12 +18,12 @@
 import static android.text.Layout.Alignment.ALIGN_NORMAL;
 
 import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 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;
@@ -120,7 +120,7 @@
         while (state.keepRunning()) {
 
             state.pauseTiming();
-            final DisplayListCanvas canvas = node.start(1200, 200);
+            final RecordingCanvas canvas = node.start(1200, 200);
             final int save = canvas.save();
             if (!mCached) Canvas.freeTextLayoutCaches();
             state.resumeTiming();
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
index ad5a34e..a7972f5 100644
--- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -17,11 +17,11 @@
 
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 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;
@@ -107,7 +107,7 @@
         while (state.keepRunning()) {
 
             state.pauseTiming();
-            final DisplayListCanvas canvas = node.start(1200, 200);
+            final RecordingCanvas canvas = node.start(1200, 200);
             final int save = canvas.save();
             if (!mCached) Canvas.freeTextLayoutCaches();
             state.resumeTiming();
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
index d98df05..00a6267 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
@@ -16,32 +16,16 @@
 
 package android.text;
 
-import static android.text.TextDirectionHeuristics.LTR;
-
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.text.Layout;
-import android.text.style.TextAppearanceSpan;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.nio.CharBuffer;
-import java.util.Random;
 import java.util.Locale;
 
 @LargeTest
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
index 1cd0ae1..33b1a47 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
@@ -16,30 +16,16 @@
 
 package android.text;
 
-import static android.text.TextDirectionHeuristics.LTR;
-
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.text.Layout;
-import android.text.style.TextAppearanceSpan;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.nio.CharBuffer;
-import java.util.Random;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class PrecomputedTextPerfTest {
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
index deb2b0a..b40dd6b 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -18,12 +18,12 @@
 import static android.text.Layout.Alignment.ALIGN_NORMAL;
 
 import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 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;
@@ -119,7 +119,7 @@
         while (state.keepRunning()) {
 
             state.pauseTiming();
-            final DisplayListCanvas canvas = node.start(1200, 200);
+            final RecordingCanvas canvas = node.start(1200, 200);
             int save = canvas.save();
             if (!mCached) Canvas.freeTextLayoutCaches();
             state.resumeTiming();
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index e1a38a0..e224fa3 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -16,30 +16,19 @@
 
 package android.text;
 
-import static android.text.TextDirectionHeuristics.LTR;
-
+import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.text.Layout;
-import android.text.style.TextAppearanceSpan;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.nio.CharBuffer;
-import java.util.Random;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class StaticLayoutPerfTest {
@@ -256,7 +245,7 @@
             state.pauseTiming();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -272,7 +261,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -288,7 +277,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -304,7 +293,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
@@ -321,7 +310,7 @@
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
@@ -339,7 +328,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -356,7 +345,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
             layout.draw(c);
@@ -373,7 +362,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
@@ -391,7 +380,7 @@
                     mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
-            final DisplayListCanvas c = node.start(1200, 200);
+            final RecordingCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
 
diff --git a/apct-tests/perftests/core/src/android/text/TextPerfUtils.java b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
index aa505b5..22e516a 100644
--- a/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
+++ b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
@@ -16,32 +16,15 @@
 
 package android.text;
 
-import static android.text.TextDirectionHeuristics.LTR;
-
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
 import android.content.res.ColorStateList;
-import android.graphics.Canvas;
 import android.graphics.Typeface;
 import android.icu.text.UnicodeSet;
 import android.icu.text.UnicodeSetIterator;
-import android.text.Layout;
 import android.text.style.TextAppearanceSpan;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.nio.CharBuffer;
-import java.util.Random;
 import java.util.ArrayList;
+import java.util.Random;
 
 public class TextPerfUtils {
 
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index 4bbe404..25cc707 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -19,13 +19,13 @@
 import static android.view.View.MeasureSpec.UNSPECIFIED;
 
 import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 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;
@@ -128,7 +128,7 @@
         while (state.keepRunning()) {
 
             state.pauseTiming();
-            final DisplayListCanvas canvas = node.start(1200, 200);
+            final RecordingCanvas canvas = node.start(1200, 200);
             int save = canvas.save();
             textView.setTextLocale(Locale.UK);
             textView.setTextLocale(Locale.US);
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
index dc34b7f..434b8e5 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -16,46 +16,29 @@
 
 package android.widget;
 
-import static android.view.View.MeasureSpec.AT_MOST;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
+import static android.widget.TextView.UNKNOWN_BORING;
 
 import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Typeface;
 import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.text.PrecomputedText;
-import android.text.Layout;
 import android.text.BoringLayout;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
+import android.text.Layout;
+import android.text.PrecomputedText;
 import android.text.TextPaint;
-import android.text.style.TextAppearanceSpan;
-import android.view.LayoutInflater;
 import android.text.TextPerfUtils;
 import android.view.View.MeasureSpec;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-
-import com.android.perftests.core.R;
-
-import java.util.Random;
-import java.util.Locale;
 
 import org.junit.Before;
-import org.junit.Test;
 import org.junit.Rule;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
-
-import static android.widget.TextView.UNKNOWN_BORING;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TextViewPrecomputedTextPerfTest {
@@ -360,7 +343,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final DisplayListCanvas c = node.start(
+            final RecordingCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
@@ -386,7 +369,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final DisplayListCanvas c = node.start(
+            final RecordingCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
@@ -414,7 +397,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final DisplayListCanvas c = node.start(
+            final RecordingCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
@@ -443,7 +426,7 @@
             textView.setText(text);
             textView.measure(width, height);
             textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
-            final DisplayListCanvas c = node.start(
+            final RecordingCanvas c = node.start(
                 textView.getMeasuredWidth(), textView.getMeasuredHeight());
             textView.nullLayouts();
             Canvas.freeTextLayoutCaches();
diff --git a/api/current.txt b/api/current.txt
index 9269d97..870c020 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -283,7 +283,6 @@
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
-    field public static final int allowForceDark = 16844172; // 0x101058c
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -638,6 +637,7 @@
     field public static final int fontVariationSettings = 16844144; // 0x1010570
     field public static final int fontWeight = 16844083; // 0x1010533
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
+    field public static final int forceDarkAllowed = 16844172; // 0x101058c
     field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
     field public static final int foreground = 16843017; // 0x1010109
     field public static final int foregroundGravity = 16843264; // 0x1010200
@@ -1199,6 +1199,7 @@
     field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
     field public static final int sessionService = 16843837; // 0x101043d
     field public static final int settingsActivity = 16843301; // 0x1010225
+    field public static final int settingsSliceUri = 16844179; // 0x1010593
     field public static final int setupActivity = 16843766; // 0x10103f6
     field public static final int shadowColor = 16843105; // 0x1010161
     field public static final int shadowDx = 16843106; // 0x1010162
@@ -1403,6 +1404,7 @@
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
     field public static final int textFontWeight = 16844165; // 0x1010585
     field public static final int textIsSelectable = 16843542; // 0x1010316
+    field public static final int textLocale = 16844178; // 0x1010592
     field public static final int textOff = 16843045; // 0x1010125
     field public static final int textOn = 16843044; // 0x1010124
     field public static final int textScaleX = 16843089; // 0x1010151
@@ -5675,6 +5677,7 @@
     method public java.lang.CharSequence getName();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
+    method public boolean hasUserSetImportance();
     method public void setBypassDnd(boolean);
     method public void setDescription(java.lang.String);
     method public void setGroup(java.lang.String);
@@ -6319,6 +6322,7 @@
     method public android.content.pm.ServiceInfo getServiceInfo();
     method public java.lang.String getServiceName();
     method public java.lang.String getSettingsActivity();
+    method public android.net.Uri getSettingsSliceUri();
     method public boolean getShowMetadataInPreview();
     method public java.lang.CharSequence loadAuthor(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
     method public java.lang.CharSequence loadContextDescription(android.content.pm.PackageManager) throws android.content.res.Resources.NotFoundException;
@@ -6522,6 +6526,8 @@
     method public java.util.List<java.lang.String> getDelegatedScopes(android.content.ComponentName, java.lang.String);
     method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
     method public java.lang.CharSequence getEndUserSessionMessage(android.content.ComponentName);
+    method public java.lang.String getGlobalPrivateDnsHost(android.content.ComponentName);
+    method public int getGlobalPrivateDnsMode(android.content.ComponentName);
     method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
     method public java.util.List<java.lang.String> getKeepUninstalledPackages(android.content.ComponentName);
     method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -6625,6 +6631,7 @@
     method public void setDelegatedScopes(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
     method public void setEndUserSessionMessage(android.content.ComponentName, java.lang.CharSequence);
+    method public void setGlobalPrivateDns(android.content.ComponentName, int, java.lang.String);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setKeepUninstalledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setKeyPairCertificate(android.content.ComponentName, java.lang.String, java.util.List<java.security.cert.Certificate>, boolean);
@@ -6801,6 +6808,10 @@
     field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
     field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
     field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
+    field public static final int PRIVATE_DNS_MODE_OFF = 1; // 0x1
+    field public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2; // 0x2
+    field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
+    field public static final int PRIVATE_DNS_MODE_UNKNOWN = 0; // 0x0
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -9160,6 +9171,7 @@
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
     method public android.net.Uri canonicalize(android.net.Uri);
+    method public final android.content.ContentProvider.CallingIdentity clearCallingIdentity();
     method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final java.lang.String getCallingPackage();
@@ -9187,6 +9199,7 @@
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
     method public boolean refresh(android.net.Uri, android.os.Bundle, android.os.CancellationSignal);
+    method public final void restoreCallingIdentity(android.content.ContentProvider.CallingIdentity);
     method protected final void setPathPermissions(android.content.pm.PathPermission[]);
     method protected final void setReadPermission(java.lang.String);
     method protected final void setWritePermission(java.lang.String);
@@ -9195,6 +9208,9 @@
     method public abstract int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
   }
 
+  public final class ContentProvider.CallingIdentity {
+  }
+
   public static abstract interface ContentProvider.PipeDataWriter<T> {
     method public abstract void writeDataToPipe(android.os.ParcelFileDescriptor, android.net.Uri, java.lang.String, android.os.Bundle, T);
   }
@@ -9309,6 +9325,7 @@
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public static boolean isSyncActive(android.accounts.Account, java.lang.String);
     method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
+    method public android.graphics.Bitmap loadThumbnail(android.net.Uri, android.util.Size, android.os.CancellationSignal) throws java.io.IOException;
     method public void notifyChange(android.net.Uri, android.database.ContentObserver);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
@@ -15302,20 +15319,9 @@
     method public java.nio.ByteBuffer getBuffer();
     method public java.io.File getFile();
     method public android.os.LocaleList getLocaleList();
+    method public int getSlant();
     method public int getTtcIndex();
     method public int getWeight();
-    method public boolean isItalic();
-    field public static final int FONT_WEIGHT_BLACK = 900; // 0x384
-    field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
-    field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
-    field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
-    field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
-    field public static final int FONT_WEIGHT_MAX = 1000; // 0x3e8
-    field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
-    field public static final int FONT_WEIGHT_MIN = 1; // 0x1
-    field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
-    field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
-    field public static final int FONT_WEIGHT_THIN = 100; // 0x64
   }
 
   public static class Font.Builder {
@@ -15328,7 +15334,7 @@
     method public android.graphics.fonts.Font build() throws java.io.IOException;
     method public android.graphics.fonts.Font.Builder setFontVariationSettings(java.lang.String);
     method public android.graphics.fonts.Font.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
-    method public android.graphics.fonts.Font.Builder setItalic(boolean);
+    method public android.graphics.fonts.Font.Builder setSlant(int);
     method public android.graphics.fonts.Font.Builder setTtcIndex(int);
     method public android.graphics.fonts.Font.Builder setWeight(int);
   }
@@ -15344,6 +15350,26 @@
     method public android.graphics.fonts.FontFamily build();
   }
 
+  public final class FontStyle {
+    ctor public FontStyle();
+    ctor public FontStyle(int, int);
+    method public int getSlant();
+    method public int getWeight();
+    field public static final int FONT_SLANT_ITALIC = 1; // 0x1
+    field public static final int FONT_SLANT_UPRIGHT = 0; // 0x0
+    field public static final int FONT_WEIGHT_BLACK = 900; // 0x384
+    field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+    field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
+    field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
+    field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
+    field public static final int FONT_WEIGHT_MAX = 1000; // 0x3e8
+    field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+    field public static final int FONT_WEIGHT_MIN = 1; // 0x1
+    field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+    field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
+    field public static final int FONT_WEIGHT_THIN = 100; // 0x64
+  }
+
   public final class FontVariationAxis {
     ctor public FontVariationAxis(java.lang.String, float);
     method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String);
@@ -15407,6 +15433,69 @@
 
 }
 
+package android.graphics.text {
+
+  public class LineBreaker {
+    method public android.graphics.text.LineBreaker.Result computeLineBreaks(android.graphics.text.MeasuredText, android.graphics.text.LineBreaker.ParagraphConstraints, int);
+    field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
+    field public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; // 0x1
+    field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0
+    field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2
+    field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0
+    field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1
+    field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1
+    field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0
+  }
+
+  public static class LineBreaker.Builder {
+    ctor public LineBreaker.Builder();
+    method public android.graphics.text.LineBreaker build();
+    method public android.graphics.text.LineBreaker.Builder setBreakStrategy(int);
+    method public android.graphics.text.LineBreaker.Builder setHyphenationFrequency(int);
+    method public android.graphics.text.LineBreaker.Builder setIndents(int[]);
+    method public android.graphics.text.LineBreaker.Builder setJustified(int);
+  }
+
+  public static class LineBreaker.ParagraphConstraints {
+    ctor public LineBreaker.ParagraphConstraints();
+    method public int getDefaultTabStop();
+    method public float getFirstWidth();
+    method public int getFirstWidthLineCount();
+    method public int[] getTabStops();
+    method public float getWidth();
+    method public void setIndent(float, int);
+    method public void setTabStops(int[], int);
+    method public void setWidth(float);
+  }
+
+  public static class LineBreaker.Result {
+    method public float getLineAscent(int);
+    method public int getLineBreakOffset(int);
+    method public int getLineCount();
+    method public float getLineDescent(int);
+    method public int getLineHyphenEdit(int);
+    method public float getLineWidth(int);
+    method public boolean hasLineTab(int);
+  }
+
+  public class MeasuredText {
+    method public void getBounds(int, int, android.graphics.Rect);
+    method public float getCharWidthAt(int);
+    method public char[] getChars();
+    method public float getWidth(int, int);
+  }
+
+  public static class MeasuredText.Builder {
+    ctor public MeasuredText.Builder(char[]);
+    method public android.graphics.text.MeasuredText.Builder appendReplacementRun(android.graphics.Paint, int, float);
+    method public android.graphics.text.MeasuredText.Builder appendStyleRun(android.graphics.Paint, int, boolean);
+    method public android.graphics.text.MeasuredText build();
+    method public android.graphics.text.MeasuredText.Builder setComputeHyphenation(boolean);
+    method public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
+  }
+
+}
+
 package android.hardware {
 
   public deprecated class Camera {
@@ -22573,6 +22662,7 @@
     method public boolean isBluetoothScoOn();
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
+    method public static boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
     method public boolean isSpeakerphoneOn();
     method public boolean isStreamMute(int);
     method public boolean isVolumeFixed();
@@ -22894,6 +22984,7 @@
     method public int getUnderrunCount();
     method public void pause() throws java.lang.IllegalStateException;
     method public void play() throws java.lang.IllegalStateException;
+    method public void registerStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
     method public void release();
     method public int reloadStaticData();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
@@ -22914,6 +23005,7 @@
     method public deprecated int setStereoVolume(float, float);
     method public int setVolume(float);
     method public void stop() throws java.lang.IllegalStateException;
+    method public void unregisterStreamEventCallback(android.media.AudioTrack.StreamEventCallback);
     method public int write(byte[], int, int);
     method public int write(byte[], int, int, int);
     method public int write(short[], int, int);
@@ -22947,6 +23039,7 @@
     method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
     method public android.media.AudioTrack.Builder setPerformanceMode(int);
     method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22970,6 +23063,13 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public static abstract class AudioTrack.StreamEventCallback {
+    ctor public AudioTrack.StreamEventCallback();
+    method public void onDataRequest(android.media.AudioTrack, int);
+    method public void onPresentationEnded(android.media.AudioTrack);
+    method public void onTearDown(android.media.AudioTrack);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -25103,6 +25203,7 @@
     method public static android.net.Uri getValidRingtoneUri(android.content.Context);
     method public int inferStreamType();
     method public static boolean isDefault(android.net.Uri);
+    method public static android.content.res.AssetFileDescriptor openDefaultRingtoneUri(android.content.Context, android.net.Uri) throws java.io.FileNotFoundException;
     method public static void setActualDefaultRingtoneUri(android.content.Context, int, android.net.Uri);
     method public deprecated void setIncludeDrm(boolean);
     method public void setStopPreviousRingtone(boolean);
@@ -27375,6 +27476,7 @@
   public static class ConnectivityManager.NetworkCallback {
     ctor public ConnectivityManager.NetworkCallback();
     method public void onAvailable(android.net.Network);
+    method public void onBlockedStatusChanged(android.net.Network, boolean);
     method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities);
     method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
     method public void onLosing(android.net.Network, int);
@@ -27646,16 +27748,16 @@
 
   public class NetworkInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public android.net.NetworkInfo.DetailedState getDetailedState();
+    method public deprecated android.net.NetworkInfo.DetailedState getDetailedState();
     method public java.lang.String getExtraInfo();
     method public deprecated java.lang.String getReason();
     method public deprecated android.net.NetworkInfo.State getState();
-    method public int getSubtype();
-    method public java.lang.String getSubtypeName();
+    method public deprecated int getSubtype();
+    method public deprecated java.lang.String getSubtypeName();
     method public deprecated int getType();
     method public deprecated java.lang.String getTypeName();
     method public deprecated boolean isAvailable();
-    method public boolean isConnected();
+    method public deprecated boolean isConnected();
     method public deprecated boolean isConnectedOrConnecting();
     method public deprecated boolean isFailover();
     method public deprecated boolean isRoaming();
@@ -29651,6 +29753,67 @@
     field public static final int EGL_WINDOW_BIT = 4; // 0x4
   }
 
+  public class EGL15 {
+    ctor public EGL15();
+    method public static int eglClientWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long);
+    method public static android.opengl.EGLImage eglCreateImage(android.opengl.EGLDisplay, android.opengl.EGLContext, int, long, long[], int);
+    method public static android.opengl.EGLSurface eglCreatePlatformPixmapSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
+    method public static android.opengl.EGLSurface eglCreatePlatformWindowSurface(android.opengl.EGLDisplay, android.opengl.EGLConfig, java.nio.Buffer, long[], int);
+    method public static android.opengl.EGLSync eglCreateSync(android.opengl.EGLDisplay, int, long[], int);
+    method public static boolean eglDestroyImage(android.opengl.EGLDisplay, android.opengl.EGLImage);
+    method public static boolean eglDestroySync(android.opengl.EGLDisplay, android.opengl.EGLSync);
+    method public static android.opengl.EGLDisplay eglGetPlatformDisplay(int, long, long[], int);
+    method public static boolean eglGetSyncAttrib(android.opengl.EGLDisplay, android.opengl.EGLSync, int, long[], int);
+    method public static boolean eglWaitSync(android.opengl.EGLDisplay, android.opengl.EGLSync, int);
+    field public static final int EGL_CL_EVENT_HANDLE = 12444; // 0x309c
+    field public static final int EGL_CONDITION_SATISFIED = 12534; // 0x30f6
+    field public static final int EGL_CONTEXT_MAJOR_VERSION = 12440; // 0x3098
+    field public static final int EGL_CONTEXT_MINOR_VERSION = 12539; // 0x30fb
+    field public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 2; // 0x2
+    field public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 1; // 0x1
+    field public static final int EGL_CONTEXT_OPENGL_DEBUG = 12720; // 0x31b0
+    field public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 12721; // 0x31b1
+    field public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 12541; // 0x30fd
+    field public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 12733; // 0x31bd
+    field public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 12722; // 0x31b2
+    field public static final long EGL_FOREVER = -1L; // 0xffffffffffffffffL
+    field public static final int EGL_GL_COLORSPACE = 12445; // 0x309d
+    field public static final int EGL_GL_COLORSPACE_LINEAR = 12426; // 0x308a
+    field public static final int EGL_GL_COLORSPACE_SRGB = 12425; // 0x3089
+    field public static final int EGL_GL_RENDERBUFFER = 12473; // 0x30b9
+    field public static final int EGL_GL_TEXTURE_2D = 12465; // 0x30b1
+    field public static final int EGL_GL_TEXTURE_3D = 12466; // 0x30b2
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 12468; // 0x30b4
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 12470; // 0x30b6
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 12472; // 0x30b8
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 12467; // 0x30b3
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 12469; // 0x30b5
+    field public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 12471; // 0x30b7
+    field public static final int EGL_GL_TEXTURE_LEVEL = 12476; // 0x30bc
+    field public static final int EGL_GL_TEXTURE_ZOFFSET = 12477; // 0x30bd
+    field public static final int EGL_IMAGE_PRESERVED = 12498; // 0x30d2
+    field public static final int EGL_LOSE_CONTEXT_ON_RESET = 12735; // 0x31bf
+    field public static final android.opengl.EGLContext EGL_NO_CONTEXT;
+    field public static final android.opengl.EGLDisplay EGL_NO_DISPLAY;
+    field public static final android.opengl.EGLImage EGL_NO_IMAGE;
+    field public static final int EGL_NO_RESET_NOTIFICATION = 12734; // 0x31be
+    field public static final android.opengl.EGLSurface EGL_NO_SURFACE;
+    field public static final android.opengl.EGLSync EGL_NO_SYNC;
+    field public static final int EGL_OPENGL_ES3_BIT = 64; // 0x40
+    field public static final int EGL_PLATFORM_ANDROID_KHR = 12609; // 0x3141
+    field public static final int EGL_SIGNALED = 12530; // 0x30f2
+    field public static final int EGL_SYNC_CL_EVENT = 12542; // 0x30fe
+    field public static final int EGL_SYNC_CL_EVENT_COMPLETE = 12543; // 0x30ff
+    field public static final int EGL_SYNC_CONDITION = 12536; // 0x30f8
+    field public static final int EGL_SYNC_FENCE = 12537; // 0x30f9
+    field public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 1; // 0x1
+    field public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 12528; // 0x30f0
+    field public static final int EGL_SYNC_STATUS = 12529; // 0x30f1
+    field public static final int EGL_SYNC_TYPE = 12535; // 0x30f7
+    field public static final int EGL_TIMEOUT_EXPIRED = 12533; // 0x30f5
+    field public static final int EGL_UNSIGNALED = 12531; // 0x30f3
+  }
+
   public class EGLConfig extends android.opengl.EGLObjectHandle {
   }
 
@@ -29670,6 +29833,9 @@
     field public static final int EGL_RECORDABLE_ANDROID = 12610; // 0x3142
   }
 
+  public class EGLImage extends android.opengl.EGLObjectHandle {
+  }
+
   public abstract class EGLObjectHandle {
     ctor protected deprecated EGLObjectHandle(int);
     ctor protected EGLObjectHandle(long);
@@ -29680,6 +29846,9 @@
   public class EGLSurface extends android.opengl.EGLObjectHandle {
   }
 
+  public class EGLSync extends android.opengl.EGLObjectHandle {
+  }
+
   public class ETC1 {
     ctor public ETC1();
     method public static void decodeBlock(java.nio.Buffer, java.nio.Buffer);
@@ -33741,6 +33910,7 @@
     field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
     field public static final java.lang.String DISALLOW_CONFIG_LOCATION = "no_config_location";
     field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+    field public static final java.lang.String DISALLOW_CONFIG_PRIVATE_DNS = "disallow_config_private_dns";
     field public static final java.lang.String DISALLOW_CONFIG_SCREEN_TIMEOUT = "no_config_screen_timeout";
     field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
     field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -36632,6 +36802,7 @@
     ctor public MediaStore();
     method public static android.net.Uri getDocumentUri(android.content.Context, android.net.Uri);
     method public static android.net.Uri getMediaScannerUri();
+    method public static android.net.Uri getMediaUri(android.content.Context, android.net.Uri);
     method public static java.lang.String getVersion(android.content.Context);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
@@ -36673,7 +36844,7 @@
 
   public static abstract interface MediaStore.Audio.AlbumColumns {
     field public static final java.lang.String ALBUM = "album";
-    field public static final java.lang.String ALBUM_ART = "album_art";
+    field public static final deprecated java.lang.String ALBUM_ART = "album_art";
     field public static final java.lang.String ALBUM_ID = "album_id";
     field public static final java.lang.String ALBUM_KEY = "album_key";
     field public static final java.lang.String ARTIST = "artist";
@@ -36795,7 +36966,7 @@
   }
 
   public static abstract interface MediaStore.Audio.PlaylistsColumns {
-    field public static final java.lang.String DATA = "_data";
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DATE_ADDED = "date_added";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String NAME = "name";
@@ -36857,15 +37028,15 @@
 
   public static class MediaStore.Images.Thumbnails implements android.provider.BaseColumns {
     ctor public MediaStore.Images.Thumbnails();
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
     method public static final android.database.Cursor query(android.content.ContentResolver, android.net.Uri, java.lang.String[]);
     method public static final android.database.Cursor queryMiniThumbnail(android.content.ContentResolver, long, int, java.lang.String[]);
     method public static final android.database.Cursor queryMiniThumbnails(android.content.ContentResolver, android.net.Uri, int, java.lang.String[]);
-    field public static final java.lang.String DATA = "_data";
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "image_id ASC";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
     field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -36880,7 +37051,7 @@
   }
 
   public static abstract interface MediaStore.MediaColumns implements android.provider.BaseColumns {
-    field public static final java.lang.String DATA = "_data";
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DATE_ADDED = "date_added";
     field public static final java.lang.String DATE_MODIFIED = "date_modified";
     field public static final java.lang.String DISPLAY_NAME = "_display_name";
@@ -36908,12 +37079,12 @@
 
   public static class MediaStore.Video.Thumbnails implements android.provider.BaseColumns {
     ctor public MediaStore.Video.Thumbnails();
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long);
-    method public static void cancelThumbnailRequest(android.content.ContentResolver, long, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long);
+    method public static deprecated void cancelThumbnailRequest(android.content.ContentResolver, long, long);
     method public static android.net.Uri getContentUri(java.lang.String);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
-    method public static android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
-    field public static final java.lang.String DATA = "_data";
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, int, android.graphics.BitmapFactory.Options);
+    method public static deprecated android.graphics.Bitmap getThumbnail(android.content.ContentResolver, long, long, int, android.graphics.BitmapFactory.Options);
+    field public static final deprecated java.lang.String DATA = "_data";
     field public static final java.lang.String DEFAULT_SORT_ORDER = "video_id ASC";
     field public static final android.net.Uri EXTERNAL_CONTENT_URI;
     field public static final int FULL_SCREEN_KIND = 2; // 0x2
@@ -42970,6 +43141,7 @@
     method public static int getDefaultSubscriptionId();
     method public static int getDefaultVoiceSubscriptionId();
     method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+    method public static int getSlotIndex(int);
     method public static int[] getSubscriptionIds(int);
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
     method public boolean isActiveSubscriptionId(int);
@@ -42990,6 +43162,7 @@
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
     field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
     field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
+    field public static final int INVALID_SIM_SLOT_INDEX = -1; // 0xffffffff
     field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
   }
 
@@ -43364,7 +43537,8 @@
 
 package android.telephony.emergency {
 
-  public final class EmergencyNumber implements android.os.Parcelable {
+  public final class EmergencyNumber implements java.lang.Comparable android.os.Parcelable {
+    method public int compareTo(android.telephony.emergency.EmergencyNumber);
     method public int describeContents();
     method public java.lang.String getCountryIso();
     method public int getEmergencyNumberSourceBitmask();
@@ -44321,6 +44495,7 @@
     method public static int lastIndexOf(java.lang.CharSequence, char, int);
     method public static int lastIndexOf(java.lang.CharSequence, char, int, int);
     method public static java.lang.CharSequence listEllipsize(android.content.Context, java.util.List<java.lang.CharSequence>, java.lang.String, android.text.TextPaint, float, int);
+    method public static java.lang.CharSequence makeSafeForPresentation(java.lang.String, int, float, int);
     method public static boolean regionMatches(java.lang.CharSequence, int, java.lang.CharSequence, int, int);
     method public static java.lang.CharSequence replace(java.lang.CharSequence, java.lang.String[], java.lang.CharSequence[]);
     method public static java.lang.String[] split(java.lang.String, java.lang.String);
@@ -44332,6 +44507,9 @@
     field public static final int CAP_MODE_SENTENCES = 16384; // 0x4000
     field public static final int CAP_MODE_WORDS = 8192; // 0x2000
     field public static final android.os.Parcelable.Creator<java.lang.CharSequence> CHAR_SEQUENCE_CREATOR;
+    field public static final int SAFE_STRING_FLAG_FIRST_LINE = 4; // 0x4
+    field public static final int SAFE_STRING_FLAG_SINGLE_LINE = 2; // 0x2
+    field public static final int SAFE_STRING_FLAG_TRIM = 1; // 0x1
   }
 
   public static abstract interface TextUtils.EllipsizeCallback {
@@ -44930,6 +45108,16 @@
     method public abstract void drawBackground(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, int);
   }
 
+  public static class LineBackgroundSpan.Standard implements android.text.style.LineBackgroundSpan android.text.ParcelableSpan {
+    ctor public LineBackgroundSpan.Standard(int);
+    ctor public LineBackgroundSpan.Standard(android.os.Parcel);
+    method public int describeContents();
+    method public void drawBackground(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, int);
+    method public final int getColor();
+    method public int getSpanTypeId();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public abstract interface LineHeightSpan implements android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
     method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
   }
@@ -45110,6 +45298,7 @@
     method public int getSpanTypeId();
     method public android.content.res.ColorStateList getTextColor();
     method public int getTextFontWeight();
+    method public android.os.LocaleList getTextLocales();
     method public int getTextSize();
     method public int getTextStyle();
     method public android.graphics.Typeface getTypeface();
@@ -48331,6 +48520,7 @@
     method public final boolean isFocusableInTouchMode();
     method public boolean isFocused();
     method public final boolean isFocusedByDefault();
+    method public boolean isForceDarkAllowed();
     method public boolean isHapticFeedbackEnabled();
     method public boolean isHardwareAccelerated();
     method public boolean isHorizontalFadingEdgeEnabled();
@@ -48517,6 +48707,7 @@
     method public void setFocusable(int);
     method public void setFocusableInTouchMode(boolean);
     method public void setFocusedByDefault(boolean);
+    method public void setForceDarkAllowed(boolean);
     method public void setForeground(android.graphics.drawable.Drawable);
     method public void setForegroundGravity(int);
     method public void setForegroundTintList(android.content.res.ColorStateList);
@@ -51261,6 +51452,7 @@
     method public default android.view.textclassifier.TextClassification classifyText(android.view.textclassifier.TextClassification.Request);
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
     method public default void destroy();
+    method public default android.view.textclassifier.TextLanguage detectLanguage(android.view.textclassifier.TextLanguage.Request);
     method public default android.view.textclassifier.TextLinks generateLinks(android.view.textclassifier.TextLinks.Request);
     method public default int getMaxGenerateLinksTextLength();
     method public default boolean isDestroyed();
@@ -51301,6 +51493,39 @@
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
   }
 
+  public final class TextLanguage implements android.os.Parcelable {
+    method public int describeContents();
+    method public float getConfidenceScore(android.icu.util.ULocale);
+    method public android.os.Bundle getExtras();
+    method public java.lang.String getId();
+    method public android.icu.util.ULocale getLocale(int);
+    method public int getLocaleHypothesisCount();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage> CREATOR;
+  }
+
+  public static final class TextLanguage.Builder {
+    ctor public TextLanguage.Builder();
+    method public android.view.textclassifier.TextLanguage build();
+    method public android.view.textclassifier.TextLanguage.Builder putLocale(android.icu.util.ULocale, float);
+    method public android.view.textclassifier.TextLanguage.Builder setExtras(android.os.Bundle);
+    method public android.view.textclassifier.TextLanguage.Builder setId(java.lang.String);
+  }
+
+  public static final class TextLanguage.Request implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public java.lang.CharSequence getText();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLanguage.Request> CREATOR;
+  }
+
+  public static final class TextLanguage.Request.Builder {
+    ctor public TextLanguage.Request.Builder(java.lang.CharSequence);
+    method public android.view.textclassifier.TextLanguage.Request build();
+    method public android.view.textclassifier.TextLanguage.Request.Builder setExtras(android.os.Bundle);
+  }
+
   public final class TextLinks implements android.os.Parcelable {
     method public int apply(android.text.Spannable, int, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan>);
     method public int describeContents();
@@ -52221,6 +52446,7 @@
     field public static final int ERROR_UNSAFE_RESOURCE = -16; // 0xfffffff0
     field public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3; // 0xfffffffd
     field public static final int ERROR_UNSUPPORTED_SCHEME = -10; // 0xfffffff6
+    field public static final int SAFE_BROWSING_THREAT_BILLING = 4; // 0x4
     field public static final int SAFE_BROWSING_THREAT_MALWARE = 1; // 0x1
     field public static final int SAFE_BROWSING_THREAT_PHISHING = 2; // 0x2
     field public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0; // 0x0
diff --git a/api/system-current.txt b/api/system-current.txt
index 92fdca4..7e51082 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -987,6 +987,7 @@
     field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
+    field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
     field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
     field public static final java.lang.String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
     field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
@@ -1012,7 +1013,6 @@
     field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
     field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
-    field public static final java.lang.String EXTRA_USER_ID = "android.intent.extra.USER_ID";
     field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
   }
 
@@ -1122,9 +1122,9 @@
     method public static void forceSafeLabels();
     method public deprecated java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadSafeLabel(android.content.pm.PackageManager, float, int);
-    field public static final int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
-    field public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
-    field public static final int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
+    field public static final deprecated int SAFE_LABEL_FLAG_FIRST_LINE = 4; // 0x4
+    field public static final deprecated int SAFE_LABEL_FLAG_SINGLE_LINE = 2; // 0x2
+    field public static final deprecated int SAFE_LABEL_FLAG_TRIM = 1; // 0x1
   }
 
   public abstract class PackageManager {
@@ -1151,7 +1151,8 @@
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
-    method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
+    method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
+    method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo);
     method public abstract void setUpdateAvailable(java.lang.String, boolean);
     method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -1245,6 +1246,22 @@
     field public int requestRes;
   }
 
+  public final class SuspendDialogInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.SuspendDialogInfo> CREATOR;
+  }
+
+  public static final class SuspendDialogInfo.Builder {
+    ctor public SuspendDialogInfo.Builder();
+    method public android.content.pm.SuspendDialogInfo build();
+    method public android.content.pm.SuspendDialogInfo.Builder setIcon(int);
+    method public android.content.pm.SuspendDialogInfo.Builder setMessage(java.lang.String);
+    method public android.content.pm.SuspendDialogInfo.Builder setMessage(int);
+    method public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(int);
+    method public android.content.pm.SuspendDialogInfo.Builder setTitle(int);
+  }
+
 }
 
 package android.content.pm.dex {
@@ -4455,6 +4472,23 @@
     field public static final java.lang.String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
   }
 
+  public static final class Telephony.Carriers implements android.provider.BaseColumns {
+    field public static final java.lang.String APN_SET_ID = "apn_set_id";
+    field public static final int CARRIER_EDITED = 4; // 0x4
+    field public static final java.lang.String EDITED = "edited";
+    field public static final java.lang.String MAX_CONNS = "max_conns";
+    field public static final java.lang.String MAX_CONNS_TIME = "max_conns_time";
+    field public static final java.lang.String MODEM_COGNITIVE = "modem_cognitive";
+    field public static final java.lang.String MTU = "mtu";
+    field public static final int NO_SET_SET = 0; // 0x0
+    field public static final int UNEDITED = 0; // 0x0
+    field public static final int USER_DELETED = 2; // 0x2
+    field public static final java.lang.String USER_EDITABLE = "user_editable";
+    field public static final int USER_EDITED = 1; // 0x1
+    field public static final java.lang.String USER_VISIBLE = "user_visible";
+    field public static final java.lang.String WAIT_TIME = "wait_time";
+  }
+
   public final class TimeZoneRulesDataContract {
     field public static final java.lang.String AUTHORITY = "com.android.timezone";
   }
@@ -5323,6 +5357,11 @@
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
+  public class PhoneStateListener {
+    method public void onRadioPowerStateChanged(int);
+    field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
+  }
+
   public class ServiceState implements android.os.Parcelable {
     method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
     method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
@@ -5450,6 +5489,7 @@
     method public boolean getEmergencyCallbackMode();
     method public java.lang.String getIsimDomain();
     method public int getPreferredNetworkType(int);
+    method public int getRadioPowerState();
     method public int getSimApplicationState();
     method public int getSimCardState();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -5464,6 +5504,7 @@
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+    method public void setCarrierDataEnabled(boolean);
     method public void setDataActivationState(int);
     method public deprecated void setDataEnabled(int, boolean);
     method public void setDataRoamingEnabled(boolean);
@@ -5513,6 +5554,9 @@
     field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
     field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
     field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
+    field public static final int RADIO_POWER_OFF = 0; // 0x0
+    field public static final int RADIO_POWER_ON = 1; // 0x1
+    field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
     field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
     field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
     field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
@@ -5579,17 +5623,12 @@
   }
 
   public final class DataProfile implements android.os.Parcelable {
-    ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
-    ctor public DataProfile(android.os.Parcel);
-    method public int describeContents();
     method public java.lang.String getApn();
     method public int getAuthType();
     method public int getBearerBitmap();
     method public int getMaxConns();
     method public int getMaxConnsTime();
     method public int getMtu();
-    method public java.lang.String getMvnoMatchData();
-    method public java.lang.String getMvnoType();
     method public java.lang.String getPassword();
     method public int getProfileId();
     method public java.lang.String getProtocol();
@@ -5599,9 +5638,8 @@
     method public java.lang.String getUserName();
     method public int getWaitTime();
     method public boolean isEnabled();
-    method public boolean isModemCognitive();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR;
+    method public boolean isPersistent();
+    method public boolean isPreferred();
     field public static final int TYPE_3GPP = 1; // 0x1
     field public static final int TYPE_3GPP2 = 2; // 0x2
     field public static final int TYPE_COMMON = 0; // 0x0
@@ -5927,11 +5965,13 @@
   }
 
   public final class ImsExternalCallState implements android.os.Parcelable {
+    ctor public ImsExternalCallState(java.lang.String, android.net.Uri, android.net.Uri, boolean, int, int, boolean);
     method public int describeContents();
     method public android.net.Uri getAddress();
     method public int getCallId();
     method public int getCallState();
     method public int getCallType();
+    method public android.net.Uri getLocalAddress();
     method public boolean isCallHeld();
     method public boolean isCallPullable();
     method public void writeToParcel(android.os.Parcel, int);
@@ -6351,7 +6391,8 @@
   }
 
   public static class MmTelFeature.MmTelCapabilities {
-    ctor public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
+    ctor public MmTelFeature.MmTelCapabilities();
+    ctor public deprecated MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities);
     ctor public MmTelFeature.MmTelCapabilities(int);
     method public final void addCapabilities(int);
     method public final boolean isCapable(int);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 2246562..4e7a114 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -91,38 +91,6 @@
 
 }
 
-package android.security.keystore.recovery {
-
-  public final class KeyChainSnapshot implements android.os.Parcelable {
-    method public deprecated byte[] getTrustedHardwarePublicKey();
-  }
-
-  public class RecoveryController {
-    method public deprecated byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
-    method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
-    method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
-  }
-
-  public class RecoverySession implements java.lang.AutoCloseable {
-    method public deprecated java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
-    method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-  }
-
-  public final class WrappedApplicationKey implements android.os.Parcelable {
-    method public deprecated byte[] getAccount();
-  }
-
-  public static class WrappedApplicationKey.Builder {
-    method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]);
-  }
-
-}
-
 package android.service.notification {
 
   public abstract class NotificationListenerService extends android.app.Service {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index c04e61b..41d546f 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -174,6 +174,8 @@
                 instrument.noWindowAnimation = true;
             } else if (opt.equals("--no-hidden-api-checks")) {
                 instrument.disableHiddenApiChecks = true;
+            } else if (opt.equals("--no-isolated-storage")) {
+                instrument.disableIsolatedStorage = true;
             } else if (opt.equals("--user")) {
                 instrument.userId = parseUserArg(nextArgRequired());
             } else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 0dade0b..70baa87 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -16,6 +16,9 @@
 
 package com.android.commands.am;
 
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
+
 import android.app.IActivityManager;
 import android.app.IInstrumentationWatcher;
 import android.app.Instrumentation;
@@ -74,16 +77,13 @@
     String logPath = null;
     public boolean noWindowAnimation = false;
     public boolean disableHiddenApiChecks = false;
+    public boolean disableIsolatedStorage = false;
     public String abi = null;
     public int userId = UserHandle.USER_CURRENT;
     public Bundle args = new Bundle();
     // Required
     public String componentNameArg;
 
-    // Disable hidden API checks for the newly started instrumentation.
-    // Must be kept in sync with ActivityManagerService.
-    private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
-
     /**
      * Construct the instrument command runner.
      */
@@ -480,7 +480,13 @@
             }
 
             // Start the instrumentation
-            int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0;
+            int flags = 0;
+            if (disableHiddenApiChecks) {
+                flags |= INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+            }
+            if (disableIsolatedStorage) {
+                flags |= INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
+            }
             if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
                         abi)) {
                 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index e915cc8..5dcb392b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -114,7 +114,7 @@
 
 void BootAnimation::onFirstRef() {
     status_t err = mSession->linkToComposerDeath(this);
-    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
+    SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
     if (err == NO_ERROR) {
         run("BootAnimation", PRIORITY_DISPLAY);
     }
@@ -128,7 +128,7 @@
 void BootAnimation::binderDied(const wp<IBinder>&)
 {
     // woah, surfaceflinger died!
-    ALOGD("SurfaceFlinger died, exiting...");
+    SLOGD("SurfaceFlinger died, exiting...");
 
     // calling requestExit() is not enough here because the Surface code
     // might be blocked on a condition variable that will never be updated.
@@ -360,7 +360,7 @@
 
 bool BootAnimation::android()
 {
-    ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+    SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
             elapsedRealtime());
     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
     initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
@@ -508,14 +508,14 @@
 static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
 {
     ZipEntryRO entry = zip->findEntryByName(name);
-    ALOGE_IF(!entry, "couldn't find %s", name);
+    SLOGE_IF(!entry, "couldn't find %s", name);
     if (!entry) {
         return false;
     }
 
     FileMap* entryMap = zip->createEntryFileMap(entry);
     zip->releaseEntry(entry);
-    ALOGE_IF(!entryMap, "entryMap is null");
+    SLOGE_IF(!entryMap, "entryMap is null");
     if (!entryMap) {
         return false;
     }
@@ -616,7 +616,7 @@
     size_t length = strftime(timeBuff, TIME_LENGTH, timeFormat, timeInfo);
 
     if (length != TIME_LENGTH - 1) {
-        ALOGE("Couldn't format time; abandoning boot animation clock");
+        SLOGE("Couldn't format time; abandoning boot animation clock");
         mClockEnabled = false;
         return;
     }
@@ -654,13 +654,13 @@
 
         char pathType;
         if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
-            // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
+            // SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
             animation.width = width;
             animation.height = height;
             animation.fps = fps;
         } else if (sscanf(l, " %c %d %d %s #%6s %16s %16s",
                           &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
-            //ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
+            //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
             //    pathType, count, pause, path, color, clockPos1, clockPos2);
             Animation::Part part;
             part.playUntilComplete = pathType == 'c';
@@ -670,7 +670,7 @@
             part.audioData = NULL;
             part.animation = NULL;
             if (!parseColor(color, part.backgroundColor)) {
-                ALOGE("> invalid color '#%s'", color);
+                SLOGE("> invalid color '#%s'", color);
                 part.backgroundColor[0] = 0.0f;
                 part.backgroundColor[1] = 0.0f;
                 part.backgroundColor[2] = 0.0f;
@@ -679,7 +679,7 @@
             animation.parts.add(part);
         }
         else if (strcmp(l, "$SYSTEM") == 0) {
-            // ALOGD("> SYSTEM");
+            // SLOGD("> SYSTEM");
             Animation::Part part;
             part.playUntilComplete = false;
             part.count = 1;
@@ -710,7 +710,7 @@
     while ((entry = zip->nextEntry(cookie)) != NULL) {
         const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
         if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
-            ALOGE("Error fetching entry file name");
+            SLOGE("Error fetching entry file name");
             continue;
         }
 
@@ -754,7 +754,7 @@
                                 }
                             }
                         } else {
-                            ALOGE("bootanimation.zip is compressed; must be only stored");
+                            SLOGE("bootanimation.zip is compressed; must be only stored");
                         }
                     }
                 }
@@ -782,7 +782,7 @@
                 frame.trimX = x;
                 frame.trimY = y;
             } else {
-                ALOGE("Error parsing trim.txt, line: %s", lineStr);
+                SLOGE("Error parsing trim.txt, line: %s", lineStr);
                 break;
             }
         }
@@ -876,7 +876,7 @@
     const int animationX = (mWidth - animation.width) / 2;
     const int animationY = (mHeight - animation.height) / 2;
 
-    ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+    SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
             elapsedRealtime());
     for (size_t i=0 ; i<pcount ; i++) {
         const Animation::Part& part(animation.parts[i]);
@@ -949,7 +949,7 @@
 
                 nsecs_t now = systemTime();
                 nsecs_t delay = frameDuration - (now - lastFrame);
-                //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
+                //SLOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
                 lastFrame = now;
 
                 if (delay > 0) {
@@ -1048,13 +1048,13 @@
 BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
 {
     if (mLoadedFiles.indexOf(fn) >= 0) {
-        ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
+        SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
             fn.string());
         return NULL;
     }
     ZipFileRO *zip = ZipFileRO::open(fn);
     if (zip == NULL) {
-        ALOGE("Failed to open animation zip \"%s\": %s",
+        SLOGE("Failed to open animation zip \"%s\": %s",
             fn.string(), strerror(errno));
         return NULL;
     }
@@ -1143,7 +1143,7 @@
     if (pollResult == 0) {
         return true;
     } else if (pollResult < 0) {
-        ALOGE("Could not poll inotify events");
+        SLOGE("Could not poll inotify events");
         return false;
     }
 
@@ -1152,7 +1152,7 @@
     if (length == 0) {
         return true;
     } else if (length < 0) {
-        ALOGE("Could not read inotify events");
+        SLOGE("Could not read inotify events");
         return false;
     }
 
@@ -1183,7 +1183,7 @@
 status_t BootAnimation::TimeCheckThread::readyToRun() {
     mInotifyFd = inotify_init();
     if (mInotifyFd < 0) {
-        ALOGE("Could not initialize inotify fd");
+        SLOGE("Could not initialize inotify fd");
         return NO_INIT;
     }
 
@@ -1191,7 +1191,7 @@
     if (mSystemWd < 0) {
         close(mInotifyFd);
         mInotifyFd = -1;
-        ALOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
+        SLOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
         return NO_INIT;
     }
 
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index db864ae..eba5586 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -33,6 +33,9 @@
 LOCAL_CFLAGS += \
         -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
 
+# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
+
 ifeq (debug,)
     LOCAL_CFLAGS += \
             -g -O0
@@ -100,6 +103,9 @@
 
 LOCAL_CFLAGS := -Werror -Wall -Wno-unused-variable -Wunused-parameter
 
+# Allow implicit fallthrough in IncidentService.cpp:85 until it is fixed.
+LOCAL_CFLAGS += -Wno-error=implicit-fallthrough
+
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
 
 LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d334f96..8fa2980 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -31,6 +31,7 @@
 #include <gui/ISurfaceComposer.h>
 
 #include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 
 #include <system/graphics.h>
@@ -74,12 +75,12 @@
     }
 }
 
-static sk_sp<SkColorSpace> dataSpaceToColorSpace(android_dataspace d)
+static sk_sp<SkColorSpace> dataSpaceToColorSpace(ui::Dataspace d)
 {
     switch (d) {
-        case HAL_DATASPACE_V0_SRGB:
+        case ui::Dataspace::V0_SRGB:
             return SkColorSpace::MakeSRGB();
-        case HAL_DATASPACE_DISPLAY_P3:
+        case ui::Dataspace::DISPLAY_P3:
             return SkColorSpace::MakeRGB(
                     SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
         default:
@@ -87,12 +88,26 @@
     }
 }
 
-static uint32_t dataSpaceToInt(android_dataspace d)
+static ui::Dataspace pickBestDataspace(ui::ColorMode colorMode)
+{
+    switch (colorMode) {
+        case ui::ColorMode::SRGB:
+            return ui::Dataspace::V0_SRGB;
+        case ui::ColorMode::DISPLAY_P3:
+        case ui::ColorMode::BT2100_PQ:
+        case ui::ColorMode::BT2100_HLG:
+            return ui::Dataspace::DISPLAY_P3;
+        default:
+            return ui::Dataspace::V0_SRGB;
+    }
+}
+
+static uint32_t dataSpaceToInt(ui::Dataspace d)
 {
     switch (d) {
-        case HAL_DATASPACE_V0_SRGB:
+        case ui::Dataspace::V0_SRGB:
             return COLORSPACE_SRGB;
-        case HAL_DATASPACE_DISPLAY_P3:
+        case ui::Dataspace::DISPLAY_P3:
             return COLORSPACE_DISPLAY_P3;
         default:
             return COLORSPACE_UNKNOWN;
@@ -161,7 +176,6 @@
 
     void* base = NULL;
     uint32_t w, s, h, f;
-    android_dataspace d;
     size_t size = 0;
 
     // Maps orientations from DisplayInfo to ISurfaceComposer
@@ -197,8 +211,15 @@
     uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
 
     sp<GraphicBuffer> outBuffer;
-    status_t result = ScreenshotClient::capture(display, Rect(), 0 /* reqWidth */,
-            0 /* reqHeight */, false, captureOrientation, &outBuffer);
+    ui::Dataspace reqDataspace =
+        pickBestDataspace(SurfaceComposerClient::getActiveColorMode(display));
+
+    // Due to the fact that we hard code the way we write pixels into screenshot,
+    // we hard code RGBA_8888 here.
+    ui::PixelFormat reqPixelFormat = ui::PixelFormat::RGBA_8888;
+    status_t result = ScreenshotClient::capture(display, reqDataspace, reqPixelFormat, Rect(),
+                                                0 /* reqWidth */, 0 /* reqHeight */, false,
+                                                captureOrientation, &outBuffer);
     if (result != NO_ERROR) {
         close(fd);
         return 1;
@@ -222,12 +243,12 @@
     h = outBuffer->getHeight();
     s = outBuffer->getStride();
     f = outBuffer->getPixelFormat();
-    d = HAL_DATASPACE_UNKNOWN;
     size = s * h * bytesPerPixel(f);
 
     if (png) {
         const SkImageInfo info =
-            SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType, dataSpaceToColorSpace(d));
+            SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType,
+                              dataSpaceToColorSpace(reqDataspace));
         SkPixmap pixmap(info, base, s * bytesPerPixel(f));
         struct FDWStream final : public SkWStream {
           size_t fBytesWritten = 0;
@@ -244,7 +265,7 @@
             notifyMediaScanner(fn);
         }
     } else {
-        uint32_t c = dataSpaceToInt(d);
+        uint32_t c = dataSpaceToInt(reqDataspace);
         write(fd, &w, 4);
         write(fd, &h, 4);
         write(fd, &f, 4);
@@ -261,4 +282,4 @@
     }
 
     return 0;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index f6b0db8..5818f5d 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -46,6 +46,7 @@
     src/logd/LogEvent.cpp \
     src/logd/LogListener.cpp \
     src/matchers/CombinationLogMatchingTracker.cpp \
+    src/matchers/EventMatcherWizard.cpp \
     src/matchers/matcher_util.cpp \
     src/matchers/SimpleLogMatchingTracker.cpp \
     src/metrics/MetricProducer.cpp \
@@ -189,6 +190,7 @@
     src/atom_field_options.proto \
     src/atoms.proto \
     src/stats_log.proto \
+    src/shell/shell_data.proto \
     tests/AlarmMonitor_test.cpp \
     tests/anomaly/AlarmTracker_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
@@ -216,6 +218,7 @@
     tests/metrics/metrics_test_helper.cpp \
     tests/statsd_test_util.cpp \
     tests/e2e/WakelockDuration_e2e_test.cpp \
+    tests/e2e/MetricActivation_e2e_test.cpp \
     tests/e2e/MetricConditionLink_e2e_test.cpp \
     tests/e2e/Alarm_e2e_test.cpp \
     tests/e2e/Attribution_e2e_test.cpp \
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 6a9e8a1..3e8b9b8 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -194,7 +194,7 @@
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
-    FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+    FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
 
@@ -219,6 +219,7 @@
 
     FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
     FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
+    FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index fb6f8c8..ce28777 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -319,7 +319,7 @@
         }
         if (!args[0].compare(String8("data-subscribe"))) {
             if (mShellSubscriber == nullptr) {
-                mShellSubscriber = new ShellSubscriber(mUidMap);
+                mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
             }
             mShellSubscriber->startNewSubscription(in, out, resultReceiver);
             return NO_ERROR;
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 453bf7e..e33bd8c 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -64,10 +64,22 @@
     optional StateField option = 1 [default = STATE_FIELD_UNSET];
 }
 
+// Used to generate StatsLog.write APIs.
+enum LogMode {
+    MODE_UNSET = 0;
+    // Log fields as their actual types e.g., all primary data types.
+    // Or fields that are hardcoded in stats_log_api_gen tool e.g., AttributionNode
+    MODE_AUTOMATIC = 1;
+    // Log fields in their proto binary format. These fields will not be parsed in statsd
+    MODE_BYTES = 2;
+}
+
 extend google.protobuf.FieldOptions {
     // Flags to decorate an atom that presents a state change.
     optional StateAtomFieldOption state_field_option = 50000;
 
     // Flags to decorate the uid fields in an atom.
     optional bool is_uid = 50001 [default = false];
+
+    optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4604510..d52dec1 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -22,6 +22,7 @@
 
 import "frameworks/base/cmds/statsd/src/atom_field_options.proto";
 import "frameworks/base/core/proto/android/app/enums.proto";
+import "frameworks/base/core/proto/android/app/settings_enums.proto";
 import "frameworks/base/core/proto/android/app/job/enums.proto";
 import "frameworks/base/core/proto/android/bluetooth/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
@@ -143,10 +144,12 @@
         BatteryCausedShutdown battery_caused_shutdown = 93;
         PhoneServiceStateChanged phone_service_state_changed = 94;
         PhoneStateChanged phone_state_changed = 95;
+        UserRestrictionChanged user_restriction_changed = 96;
+        SettingsUIChanged settings_ui_changed = 97;
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10025
+    // Next: 10037
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000;
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -183,6 +186,8 @@
         DiskIo disk_io = 10032;
         PowerProfile power_profile = 10033;
         ProcStats proc_stats_pkg_proc = 10034;
+        ProcessCpuTime process_cpu_time = 10035;
+        NativeProcessMemoryState native_process_memory_state = 10036;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -1435,6 +1440,40 @@
 }
 
 /**
+ * Logs when Settings UI has changed.
+ *
+ * Logged from:
+ *   packages/apps/Settings
+ */
+message SettingsUIChanged {
+    /**
+     * Where this SettingsUIChange event comes from. For example, if
+     * it's a PAGE_VISIBLE event, where the page is opened from.
+     */
+    optional android.app.settings.PageId attribution = 1;
+
+    /**
+     * What the UI action is.
+     */
+    optional android.app.settings.Action action = 2;
+
+    /**
+     * Where the action is happening
+     */
+    optional android.app.settings.PageId pageId = 3;
+
+    /**
+     * What preference changed in this event.
+     */
+    optional string changedPreferenceKey = 4;
+
+    /**
+     * The new value of the changed preference.
+     */
+    optional int64 changedPreferenceIntValue = 5;
+}
+
+/**
  * Logs that a setting was updated.
  * Logged from:
  *   frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -2049,25 +2088,34 @@
     optional int32 importance = 7;
 
     // ID for the notification channel.
-    optional int32 channel_id = 8;
+    optional string channel_id = 8;
 
     // Importance for the notification channel.
     optional int32 channel_importance = 9;
 
+    // Application-supplied ID associated with the notifications group.
+    optional string group_id = 10;
+
     // Whether notification was a group summary.
-    optional bool group_summary = 10;
+    optional bool group_summary = 11;
 
-    // Time since notification was created in milliseconds.
-    optional int64 since_create_millis = 11;
+    // Reason for dismissal of a notification. This field is only meaningful for
+    // TYPE_DISMISS events.
+    optional int32 dismiss_reason = 12;
 
-    // Time since notification was interrupted in milliseconds.
-    optional int64 since_interruption_millis = 12;
+    // The first post time of notification, stable across updates.
+    optional int64 creation_millis = 13;
 
-    // Time since notification was updated in milliseconds.
-    optional int64 since_update_millis = 13;
+    // The most recent interruption time, or the creation time if no updates.
+    // Differs from update_millis because updates are filtered based on whether
+    // they actually interrupted the user.
+    optional int64 interruption_millis = 14;
 
-    // Time since notification was visible in milliseconds.
-    optional int64 since_visible_millis = 14;
+    // The most recent update time, or the creation time if no updates.
+    optional int64 update_millis = 15;
+
+    // The most recent visibility event.
+    optional int64 visible_millis = 16;
 }
 
 
@@ -2344,6 +2392,31 @@
 }
 
 /*
+ * Logs the memory stats for a native process (from procfs).
+ */
+message NativeProcessMemoryState {
+  // The uid if available. -1 means not available.
+  optional int32 uid = 1 [(is_uid) = true];
+
+  // The process name.
+  optional string process_name = 2;
+
+  // # of page-faults
+  optional int64 page_fault = 3;
+
+  // # of major page-faults
+  optional int64 page_major_fault = 4;
+
+  // RSS
+  optional int64 rss_in_bytes = 5;
+
+  // RSS high watermark.
+  // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
+  // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
+  optional int64 rss_high_watermark_in_bytes = 6;
+}
+
+/*
  * Elapsed real time from SystemClock.
  */
 message SystemElapsedRealtime {
@@ -3006,3 +3079,33 @@
 message PowerProfile {
     optional PowerProfileProto power_profile = 1;
 }
+
+/**
+ * Logs when a user restriction was added or removed.
+ *
+ * Logged from:
+ *   frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ */
+message UserRestrictionChanged {
+    // The raw string of the user restriction as defined in UserManager.
+    // Allowed values are defined in UserRestrictionsUtils#USER_RESTRICTIONS.
+    optional string restriction = 1;
+    // Whether the restriction is enabled or disabled.
+    optional bool enabled = 2;
+}
+
+/**
+ * Pulls process user time and system time. Puller takes a snapshot of all pids
+ * in the system and returns cpu stats for those that are working at the time.
+ * Dead pids will be dropped. Kernel processes are excluded.
+ * Min cool-down is 5 sec.
+ */
+message ProcessCpuTime {
+    optional int32 uid = 1 [(is_uid) = true];
+
+    optional string process_name = 2;
+    // Process cpu time in user space, cumulative from boot/process start
+    optional int64 user_time_millis = 3;
+    // Process cpu time in system space, cumulative from boot/process start
+    optional int64 system_time_millis = 4;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index dab64ca..ba626f8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -170,6 +170,12 @@
           {2, 3},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
+        // native_process_memory_state
+        {android::util::NATIVE_PROCESS_MEMORY_STATE,
+         {{3, 4, 5, 6},
+          {2},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         // temperature
         {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
         // binder_calls
@@ -223,6 +229,11 @@
         // PowerProfile constants for power model calculations.
         {android::util::POWER_PROFILE,
          {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+        // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
+        {android::util::PROCESS_CPU_TIME,
+            {{} /* additive fields */, {} /* non additive fields */,
+             5 * NS_PER_SEC /* min cool-down in seconds*/,
+             new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.cpp b/cmds/statsd/src/matchers/EventMatcherWizard.cpp
new file mode 100644
index 0000000..8418e98
--- /dev/null
+++ b/cmds/statsd/src/matchers/EventMatcherWizard.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "EventMatcherWizard.h"
+#include <unordered_set>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+using std::string;
+using std::vector;
+
+MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcher_index) {
+    if (matcher_index < 0 || matcher_index >= (int)mAllEventMatchers.size()) {
+        return MatchingState::kNotComputed;
+    }
+    vector<MatchingState> matcherCache(mAllEventMatchers.size(), MatchingState::kNotComputed);
+    mAllEventMatchers[matcher_index]->onLogEvent(event, mAllEventMatchers, matcherCache);
+    return matcherCache[matcher_index];
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/matchers/EventMatcherWizard.h b/cmds/statsd/src/matchers/EventMatcherWizard.h
new file mode 100644
index 0000000..57ec2b3
--- /dev/null
+++ b/cmds/statsd/src/matchers/EventMatcherWizard.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "LogMatchingTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class EventMatcherWizard : public virtual android::RefBase {
+public:
+    EventMatcherWizard(){};  // for testing
+    EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers)
+        : mAllEventMatchers(eventTrackers){};
+
+    virtual ~EventMatcherWizard(){};
+
+    MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
+
+private:
+    std::vector<sp<LogMatchingTracker>> mAllEventMatchers;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 02b9773..f5a16e9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -69,11 +69,16 @@
 
 GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
                                          const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard, const int pullTagId,
+                                         const sp<ConditionWizard>& wizard,
+                                         const int whatMatcherIndex,
+                                         const sp<EventMatcherWizard>& matcherWizard,
+                                         const int pullTagId,
                                          const int triggerAtomId, const int atomId,
                                          const int64_t timeBaseNs, const int64_t startTimeNs,
                                          const sp<StatsPullerManager>& pullerManager)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+      mWhatMatcherIndex(whatMatcherIndex),
+      mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
       mPullTagId(pullTagId),
       mTriggerAtomId(triggerAtomId),
@@ -136,7 +141,7 @@
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
     if (mIsPulled) {
-        pullLocked(startTimeNs);
+        pullAndMatchEventsLocked(startTimeNs);
     }
 
     VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -302,7 +307,7 @@
     mPastBuckets.clear();
 }
 
-void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
+void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
     bool triggerPuller = false;
     switch(mSamplingType) {
         // When the metric wants to do random sampling and there is already one gauge atom for the
@@ -331,7 +336,10 @@
         return;
     }
     for (const auto& data : allData) {
-        onMatchedLogEventLocked(0, *data);
+        if (mEventMatcherWizard->matchLogEvent(
+                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+        }
     }
 }
 
@@ -341,7 +349,7 @@
     flushIfNeededLocked(eventTimeNs);
     mCondition = conditionMet;
     if (mIsPulled) {
-        pullLocked(eventTimeNs);
+        pullAndMatchEventsLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
 }
 
@@ -354,7 +362,7 @@
     // pull for every dimension.
     mCondition = overallCondition;
     if (mIsPulled) {
-        pullLocked(eventTimeNs);
+        pullAndMatchEventsLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
 }
 
@@ -387,7 +395,10 @@
         return;
     }
     for (const auto& data : allData) {
-        onMatchedLogEventLocked(0, *data);
+        if (mEventMatcherWizard->matchLogEvent(
+                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+        }
     }
 }
 
@@ -426,7 +437,7 @@
     flushIfNeededLocked(eventTimeNs);
 
     if (mTriggerAtomId == event.GetTagId()) {
-        pullLocked(eventTimeNs);
+        pullAndMatchEventsLocked(eventTimeNs);
         return;
     }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6379389..99827bb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -24,6 +24,7 @@
 #include "../external/PullDataReceiver.h"
 #include "../external/StatsPullerManager.h"
 #include "../matchers/matcher_util.h"
+#include "../matchers/EventMatcherWizard.h"
 #include "MetricProducer.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "../stats_util.h"
@@ -56,7 +57,9 @@
 class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
+                        const int whatMatcherIndex,
+                        const sp<EventMatcherWizard>& matcherWizard,
                         const int pullTagId, const int triggerAtomId, const int atomId,
                         const int64_t timeBaseNs, const int64_t startTimeNs,
                         const sp<StatsPullerManager>& pullerManager);
@@ -78,7 +81,7 @@
         flushCurrentBucketLocked(eventTimeNs);
         mCurrentBucketStartTimeNs = eventTimeNs;
         if (mIsPulled) {
-            pullLocked(eventTimeNs);
+            pullAndMatchEventsLocked(eventTimeNs);
         }
     };
 
@@ -113,7 +116,11 @@
 
     void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
 
-    void pullLocked(const int64_t timestampNs);
+    void pullAndMatchEventsLocked(const int64_t timestampNs);
+
+    const int mWhatMatcherIndex;
+
+    sp<EventMatcherWizard> mEventMatcherWizard;
 
     sp<StatsPullerManager> mPullerManager;
     // tagId for pulled data. -1 if this is not pulled
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index df08181..f87849e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -64,8 +64,54 @@
         onMatchedLogEventInternalLocked(
                 matcherIndex, metricKey, conditionKey, condition, event);
     }
+}
 
- }
+bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
+    bool isActive = mEventActivationMap.empty();
+    for (auto& it : mEventActivationMap) {
+        if (it.second.state == ActivationState::kActive &&
+            elapsedTimestampNs > it.second.ttl_ns + it.second.activation_ns) {
+            it.second.state = ActivationState::kNotActive;
+        }
+        if (it.second.state == ActivationState::kActive) {
+            isActive = true;
+        }
+    }
+    return isActive;
+}
+
+void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mIsActive) {
+        return;
+    }
+    mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
+    if (!mIsActive) {
+        flushLocked(elapsedTimestampNs);
+    }
+}
+
+void MetricProducer::addActivation(int activationTrackerIndex, int64_t ttl_seconds) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    // When a metric producer does not depend on any activation, its mIsActive is true.
+    // Therefor, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
+    // change.
+    if  (mEventActivationMap.empty()) {
+        mIsActive = false;
+    }
+    mEventActivationMap[activationTrackerIndex].ttl_ns = ttl_seconds * NS_PER_SEC;
+}
+
+void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
+    auto it = mEventActivationMap.find(activationTrackerIndex);
+    if (it == mEventActivationMap.end()) {
+        return;
+    }
+    it->second.activation_ns = elapsedTimestampNs;
+    it->second.state = ActivationState::kActive;
+    mIsActive = true;
+}
+
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 6fe4bfb..b21fd50 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -34,6 +34,17 @@
 namespace os {
 namespace statsd {
 
+// If the metric has no activation requirement, it will be active once the metric producer is
+// created.
+// If the metric needs to be activated by atoms, the metric producer will start
+// with kNotActive state, turn to kActive when the activation event arrives, become kNotActive
+// when it reaches the duration limit (timebomb). If the activation event arrives again before
+// or after it expires, the event producer will be re-activated and ttl will be reset.
+enum ActivationState {
+    kNotActive = 0,
+    kActive = 1,
+};
+
 // A MetricProducer is responsible for compute one single metrics, creating stats log report, and
 // writing the report to dropbox. MetricProducers should respond to package changes as required in
 // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
@@ -54,7 +65,8 @@
           mContainANYPositionInDimensionsInWhat(false),
           mSliceByPositionALL(false),
           mSameConditionDimensionsInTracker(false),
-          mHasLinksToAllConditionDimensionsInTracker(false) {
+          mHasLinksToAllConditionDimensionsInTracker(false),
+          mIsActive(true) {
     }
 
     virtual ~MetricProducer(){};
@@ -93,17 +105,23 @@
     // Consume the parsed stats log entry that already matched the "what" of the metric.
     void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
         std::lock_guard<std::mutex> lock(mMutex);
-        onMatchedLogEventLocked(matcherIndex, event);
+        if (mIsActive) {
+            onMatchedLogEventLocked(matcherIndex, event);
+        }
     }
 
     void onConditionChanged(const bool condition, const int64_t eventTime) {
         std::lock_guard<std::mutex> lock(mMutex);
-        onConditionChangedLocked(condition, eventTime);
+        if (mIsActive) {
+            onConditionChangedLocked(condition, eventTime);
+        }
     }
 
     void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
         std::lock_guard<std::mutex> lock(mMutex);
-        onSlicedConditionMayChangeLocked(overallCondition, eventTime);
+        if (mIsActive) {
+            onSlicedConditionMayChangeLocked(overallCondition, eventTime);
+        }
     }
 
     bool isConditionSliced() const {
@@ -177,6 +195,15 @@
         return mCurrentBucketNum;
     }
 
+    void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        activateLocked(activationTrackerIndex, elapsedTimestampNs);
+    }
+
+    void addActivation(int activationTrackerIndex, int64_t ttl_seconds);
+
+    void flushIfExpire(int64_t elapsedTimestampNs);
+
 protected:
     virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
     virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -189,6 +216,10 @@
     virtual size_t byteSizeLocked() const = 0;
     virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
 
+    bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
+
+    void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+
     /**
      * Flushes the current bucket if the eventTime is after the current bucket's end time. This will
        also flush the current partial bucket in memory.
@@ -198,9 +229,9 @@
     /**
      * Flushes all the data including the current partial bucket.
      */
-    virtual void flushLocked(const int64_t& eventTime) {
-        flushIfNeededLocked(eventTime);
-        flushCurrentBucketLocked(eventTime);
+    virtual void flushLocked(const int64_t& eventTimeNs) {
+        flushIfNeededLocked(eventTimeNs);
+        flushCurrentBucketLocked(eventTimeNs);
     };
 
     /**
@@ -295,6 +326,21 @@
     virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
 
     mutable std::mutex mMutex;
+
+    struct Activation {
+        Activation() : ttl_ns(0), activation_ns(0), state(ActivationState::kNotActive)  {}
+
+        int64_t ttl_ns;
+        int64_t activation_ns;
+        ActivationState state;
+    };
+    // When the metric producer has multiple activations, these activations are ORed to determine
+    // whether the metric producer is ready to generate metrics.
+    std::unordered_map<int, Activation> mEventActivationMap;
+
+    bool mIsActive;
+
+    FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 0e5ef4d..f85ba1f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -73,7 +73,8 @@
             key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
             timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
             mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
-            mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mNoReportMetricIds);
+            mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
+            mActivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
 
@@ -298,7 +299,12 @@
     }
 
     int tagId = event.GetTagId();
-    int64_t eventTime = event.GetElapsedTimestampNs();
+    int64_t eventTimeNs = event.GetElapsedTimestampNs();
+
+    for (int metric : mMetricIndexesWithActivation) {
+        mAllMetricProducers[metric]->flushIfExpire(eventTimeNs);
+    }
+
     if (mTagIds.find(tagId) == mTagIds.end()) {
         // not interesting...
         return;
@@ -310,6 +316,14 @@
         matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
     }
 
+    for (const auto& it : mActivationAtomTrackerToMetricMap) {
+        if (matcherCache[it.first] == MatchingState::kMatched) {
+            for (int metricIndex : it.second) {
+                mAllMetricProducers[metricIndex]->activate(it.first, eventTimeNs);
+            }
+        }
+    }
+
     // A bitmap to see which ConditionTracker needs to be re-evaluated.
     vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
 
@@ -347,13 +361,13 @@
                 // Push the new condition to it directly.
                 if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
                     mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
-                                                                         eventTime);
+                                                                         eventTimeNs);
                     // metric cares about sliced conditions, and it may have changed. Send
                     // notification, and the metric can query the sliced conditions that are
                     // interesting to it.
                 } else {
                     mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
-                                                                                 eventTime);
+                                                                                 eventTimeNs);
                 }
             }
         }
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index dfbb69f..649222ff 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -195,6 +195,11 @@
     // maps from ConditionTracker to MetricProducer
     std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
 
+    // maps from life span triggering event to MetricProducers.
+    std::unordered_map<int, std::vector<int>> mActivationAtomTrackerToMetricMap;
+
+    std::vector<int> mMetricIndexesWithActivation;
+
     void initLogSourceWhiteList();
 
     // The metrics that don't need to be uploaded or even reported.
@@ -207,7 +212,7 @@
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
-    FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+    FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
     FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
     FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
@@ -230,6 +235,7 @@
 
     FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
     FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
+    FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 75d6df9..136ba07 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -25,6 +25,7 @@
 #include "../external/StatsPullerManager.h"
 #include "../matchers/CombinationLogMatchingTracker.h"
 #include "../matchers/SimpleLogMatchingTracker.h"
+#include "../matchers/EventMatcherWizard.h"
 #include "../metrics/CountMetricProducer.h"
 #include "../metrics/DurationMetricProducer.h"
 #include "../metrics/EventMetricProducer.h"
@@ -294,6 +295,7 @@
                  unordered_map<int, std::vector<int>>& trackerToMetricMap,
                  unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
     sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+    sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
     const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
                                 config.event_metric_size() + config.value_metric_size();
     allMetricProducers.reserve(allMetricsCount);
@@ -563,7 +565,8 @@
         }
 
         sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, wizard, pullTagId, triggerAtomId, atomTagId,
+                key, metric, conditionIndex, wizard,
+                trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
                 timeBaseTimeNs, currentTimeNs, pullerManager);
         allMetricProducers.push_back(gaugeProducer);
     }
@@ -682,6 +685,44 @@
     return true;
 }
 
+bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config,
+                           const int64_t currentTimeNs,
+                           const unordered_map<int64_t, int> &logEventTrackerMap,
+                           const unordered_map<int64_t, int> &metricProducerMap,
+                           vector<sp<MetricProducer>>& allMetricProducers,
+                           unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+                           vector<int>& metricsWithActivation) {
+    for (int i = 0; i < config.metric_activation_size(); ++i) {
+        const MetricActivation& metric_activation = config.metric_activation(i);
+        auto itr = metricProducerMap.find(metric_activation.metric_id());
+        if (itr == metricProducerMap.end()) {
+            ALOGE("Metric id not found in metric activation: %lld",
+                (long long)metric_activation.metric_id());
+            return false;
+        }
+        const int metricTrackerIndex = itr->second;
+        if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) {
+            ALOGE("Invalid metric tracker index.");
+            return false;
+        }
+        metricsWithActivation.push_back(metricTrackerIndex);
+        for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
+            const EventActivation& activation = metric_activation.event_activation(j);
+            auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id());
+            if (logTrackerIt == logEventTrackerMap.end()) {
+                ALOGE("Atom matcher not found for event activation.");
+                return false;
+            }
+            const int atomMatcherIndex = logTrackerIt->second;
+            activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
+                metricTrackerIndex);
+            allMetricProducers[metricTrackerIndex]->addActivation(
+                atomMatcherIndex, activation.ttl_seconds());
+        }
+    }
+    return true;
+}
+
 bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
                       const sp<StatsPullerManager>& pullerManager,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -695,6 +736,8 @@
                       unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+                      vector<int>& metricsWithActivation,
                       std::set<int64_t>& noReportMetricIds) {
     unordered_map<int64_t, int> logTrackerMap;
     unordered_map<int64_t, int> conditionTrackerMap;
@@ -729,6 +772,11 @@
         ALOGE("initAlarms failed");
         return false;
     }
+    if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
+            allMetricProducers, activationAtomTrackerToMetricMap, metricsWithActivation)) {
+        ALOGE("initMetricActivations failed");
+        return false;
+    }
 
     return true;
 }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index c660149..9ffceda 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -108,6 +108,8 @@
                       std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      unordered_map<int, std::vector<int>>& lifeSpanEventTrackerToMetricMap,
+                      vector<int>& metricsWithLifeSpan,
                       std::set<int64_t>& noReportMetricIds);
 
 bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 1306a46..dffff7a 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -18,9 +18,9 @@
 
 #include "ShellSubscriber.h"
 
-#include "matchers/matcher_util.h"
-
 #include <android-base/file.h>
+#include "matchers/matcher_util.h"
+#include "stats_log_util.h"
 
 using android::util::ProtoOutputStream;
 
@@ -28,6 +28,8 @@
 namespace os {
 namespace statsd {
 
+const static int FIELD_ID_ATOM = 1;
+
 void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver) {
     VLOG("start new shell subscription");
     {
@@ -42,25 +44,106 @@
         IInterface::asBinder(mResultReceiver)->linkToDeath(this);
     }
 
-    // Spawn another thread to read the config updates from the input file descriptor
-    std::thread reader([in, this] { readConfig(in); });
-    reader.detach();
+    // Note that the following is blocking, and it's intended as we cannot return until the shell
+    // cmd exits, otherwise all resources & FDs will be automatically closed.
 
+    // Read config forever until EOF is reached. Clients may send multiple configs -- each new
+    // config replace the previous one.
+    readConfig(in);
+
+    // Now we have read an EOF we now wait for the semaphore until the client exits.
+    VLOG("Now wait for client to exit");
     std::unique_lock<std::mutex> lk(mMutex);
-
     mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
-    if (reader.joinable()) {
-        reader.join();
-    }
 }
 
 void ShellSubscriber::updateConfig(const ShellSubscription& config) {
     std::lock_guard<std::mutex> lock(mMutex);
     mPushedMatchers.clear();
+    mPulledInfo.clear();
+
     for (const auto& pushed : config.pushed()) {
         mPushedMatchers.push_back(pushed);
         VLOG("adding matcher for atom %d", pushed.atom_id());
     }
+
+    int64_t token = getElapsedRealtimeNs();
+    mPullToken = token;
+
+    int64_t minInterval = -1;
+    for (const auto& pulled : config.pulled()) {
+        // All intervals need to be multiples of the min interval.
+        if (minInterval < 0 || pulled.freq_millis() < minInterval) {
+            minInterval = pulled.freq_millis();
+        }
+
+        mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis());
+        VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
+    }
+
+    if (mPulledInfo.size() > 0 && minInterval > 0) {
+        // This thread is guaranteed to terminate after it detects the token is different or
+        // cleaned up.
+        std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
+        puller.detach();
+    }
+}
+
+void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+                                          const SimpleAtomMatcher& matcher) {
+    if (mOutput == 0) return;
+    int count = 0;
+    mProto.clear();
+    for (const auto& event : data) {
+        VLOG("%s", event->ToString().c_str());
+        if (matchesSimple(*mUidMap, matcher, *event)) {
+            VLOG("matched");
+            count++;
+            uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+                                              util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+            event->ToProto(mProto);
+            mProto.end(atomToken);
+        }
+    }
+
+    if (count > 0) {
+        // First write the payload size.
+        size_t bufferSize = mProto.size();
+        write(mOutput, &bufferSize, sizeof(bufferSize));
+        VLOG("%d atoms, proto size: %zu", count, bufferSize);
+        // Then write the payload.
+        mProto.flush(mOutput);
+    }
+    mProto.clear();
+}
+
+void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
+    while (1) {
+        int64_t nowMillis = getElapsedRealtimeMillis();
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            if (mPulledInfo.size() == 0 || mPullToken != token) {
+                VLOG("Pulling thread %lld done!", (long long)token);
+                return;
+            }
+            for (auto& pullInfo : mPulledInfo) {
+                if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval < nowMillis) {
+                    VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
+
+                    vector<std::shared_ptr<LogEvent>> data;
+                    mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), nowMillis * 1000000L,
+                                     &data);
+                    VLOG("pulled %zu atoms", data.size());
+                    if (data.size() > 0) {
+                        writeToOutputLocked(data, pullInfo.mPullerMatcher);
+                    }
+                    pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
+                }
+            }
+        }
+        VLOG("Pulling thread %lld sleep....", (long long)token);
+        std::this_thread::sleep_for(std::chrono::milliseconds(intervalMillis));
+    }
 }
 
 void ShellSubscriber::readConfig(int in) {
@@ -101,6 +184,8 @@
     mOutput = 0;
     mResultReceiver = nullptr;
     mPushedMatchers.clear();
+    mPulledInfo.clear();
+    mPullToken = 0;
     VLOG("done clean up");
 }
 
@@ -110,10 +195,13 @@
     if (mOutput <= 0) {
         return;
     }
-
     for (const auto& matcher : mPushedMatchers) {
         if (matchesSimple(*mUidMap, matcher, event)) {
+            VLOG("%s", event.ToString().c_str());
+            uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+                                              util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
             event.ToProto(mProto);
+            mProto.end(atomToken);
             // First write the payload size.
             size_t bufferSize = mProto.size();
             write(mOutput, &bufferSize, sizeof(bufferSize));
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 0ace35f..5401f31 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -24,6 +24,7 @@
 #include <mutex>
 #include <string>
 #include <thread>
+#include "external/StatsPullerManager.h"
 #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "packages/UidMap.h"
@@ -51,14 +52,15 @@
  * with sizeof(size_t) bytes indicating the size of the proto message payload.
  *
  * The stream would be in the following format:
- * |size_t|atom1 proto|size_t|atom2 proto|....
+ * |size_t|shellData proto|size_t|shellData proto|....
  *
  * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
  * until it exits.
  */
 class ShellSubscriber : public virtual IBinder::DeathRecipient {
 public:
-    ShellSubscriber(sp<UidMap> uidMap) : mUidMap(uidMap){};
+    ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
+        : mUidMap(uidMap), mPullerMgr(pullerMgr){};
 
     /**
      * Start a new subscription.
@@ -70,15 +72,28 @@
     void onLogEvent(const LogEvent& event);
 
 private:
+    struct PullInfo {
+        PullInfo(const SimpleAtomMatcher& matcher, int64_t interval)
+            : mPullerMatcher(matcher), mInterval(interval), mPrevPullElapsedRealtimeMs(0) {
+        }
+        SimpleAtomMatcher mPullerMatcher;
+        int64_t mInterval;
+        int64_t mPrevPullElapsedRealtimeMs;
+    };
     void readConfig(int in);
 
     void updateConfig(const ShellSubscription& config);
 
+    void startPull(int64_t token, int64_t intervalMillis);
+
     void cleanUpLocked();
 
+    void writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+                             const SimpleAtomMatcher& matcher);
+
     sp<UidMap> mUidMap;
 
-    // bool mWritten = false;
+    sp<StatsPullerManager> mPullerMgr;
 
     android::util::ProtoOutputStream mProto;
 
@@ -93,6 +108,10 @@
     sp<IResultReceiver> mResultReceiver;
 
     std::vector<SimpleAtomMatcher> mPushedMatchers;
+
+    std::vector<PullInfo> mPulledInfo;
+
+    int64_t mPullToken = 0;  // A unique token to identify a puller thread.
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
index 516693d..73cb49a 100644
--- a/cmds/statsd/src/shell/shell_config.proto
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -24,7 +24,7 @@
 import "frameworks/base/cmds/statsd/src/statsd_config.proto";
 
 message PulledAtomSubscription {
-    optional int32 atom_id = 1;
+    optional SimpleAtomMatcher matcher = 1;
 
     /* gap between two pulls in milliseconds */
     optional int32 freq_millis = 2;
diff --git a/cmds/statsd/src/shell/shell_data.proto b/cmds/statsd/src/shell/shell_data.proto
new file mode 100644
index 0000000..236bdbd
--- /dev/null
+++ b/cmds/statsd/src/shell/shell_data.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+
+option java_package = "com.android.os.statsd";
+option java_outer_classname = "ShellDataProto";
+
+import "frameworks/base/cmds/statsd/src/atoms.proto";
+
+// The output of shell subscription, including both pulled and pushed subscriptions.
+message ShellData {
+    repeated Atom atom = 1;
+}
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 805e583..2498d9f 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -25,15 +25,16 @@
 #include <utils/Log.h>
 #include <utils/SystemClock.h>
 
+using android::util::AtomsInfo;
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FIXED64;
 using android::util::FIELD_TYPE_FLOAT;
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_INT64;
-using android::util::FIELD_TYPE_UINT64;
-using android::util::FIELD_TYPE_FIXED64;
 using android::util::FIELD_TYPE_MESSAGE;
 using android::util::FIELD_TYPE_STRING;
+using android::util::FIELD_TYPE_UINT64;
 using android::util::ProtoOutputStream;
 
 namespace android {
@@ -294,8 +295,9 @@
 // }
 //
 //
-void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size_t* index,
-                                       int depth, int prefix, ProtoOutputStream* protoOutput) {
+void writeFieldValueTreeToStreamHelper(int tagId, const std::vector<FieldValue>& dims,
+                                       size_t* index, int depth, int prefix,
+                                       ProtoOutputStream* protoOutput) {
     size_t count = dims.size();
     while (*index < count) {
         const auto& dim = dims[*index];
@@ -319,9 +321,31 @@
                 case FLOAT:
                     protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
                     break;
-                case STRING:
-                    protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
+                case STRING: {
+                    bool isBytesField = false;
+                    // Bytes field is logged via string format in log_msg format. So here we check
+                    // if this string field is a byte field.
+                    std::map<int, std::vector<int>>::const_iterator itr;
+                    if (depth == 0 && (itr = AtomsInfo::kBytesFieldAtoms.find(tagId)) !=
+                                              AtomsInfo::kBytesFieldAtoms.end()) {
+                        const std::vector<int>& bytesFields = itr->second;
+                        for (int bytesField : bytesFields) {
+                            if (bytesField == fieldNum) {
+                                // This is a bytes field
+                                isBytesField = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (isBytesField) {
+                        protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+                                           (const char*)dim.mValue.str_value.c_str(),
+                                           dim.mValue.str_value.length());
+                    } else {
+                        protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
+                    }
                     break;
+                }
                 case STORAGE:
                     protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
                                        (const char*)dim.mValue.storage_value.data(),
@@ -342,7 +366,7 @@
             }
             // Directly jump to the leaf value because the repeated position field is implied
             // by the position of the sub msg in the parent field.
-            writeFieldValueTreeToStreamHelper(dims, index, valueDepth,
+            writeFieldValueTreeToStreamHelper(tagId, dims, index, valueDepth,
                                               dim.mField.getPrefix(valueDepth), protoOutput);
             if (msg_token != 0) {
                 protoOutput->end(msg_token);
@@ -359,7 +383,7 @@
     uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
 
     size_t index = 0;
-    writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput);
+    writeFieldValueTreeToStreamHelper(tagId, values, &index, 0, 0, protoOutput);
     protoOutput->end(atomToken);
 }
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index d19e247..d5f81a59 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -347,6 +347,17 @@
   optional float probability_of_informing = 7 [default = 1.1];
 }
 
+message EventActivation {
+  optional int64 atom_matcher_id = 1;
+  optional int64 ttl_seconds = 2;
+}
+
+message MetricActivation {
+  optional int64 metric_id = 1;
+
+  repeated EventActivation event_activation = 2;
+}
+
 message StatsdConfig {
   optional int64 id = 1;
 
@@ -384,6 +395,8 @@
 
   optional bool hash_strings_in_metric_report = 16 [default = true];
 
+  repeated MetricActivation metric_activation = 17;
+
   // Field number 1000 is reserved for later use.
   reserved 1000;
 }
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 8fbb58a..f8184d8 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -282,13 +282,17 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                 trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                 trackerToMetricMap, trackerToConditionMap,
+                                 lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                 noReportMetricIds));
     EXPECT_EQ(1u, allMetricProducers.size());
     EXPECT_EQ(1u, allAnomalyTrackers.size());
     EXPECT_EQ(1u, noReportMetricIds.size());
@@ -309,13 +313,17 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                   periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                   allAtomMatchers, allConditionTrackers, allMetricProducers,
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                  trackerToMetricMap, trackerToConditionMap,
+                                  lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -333,13 +341,17 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                   periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                   allAtomMatchers, allConditionTrackers, allMetricProducers,
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                  trackerToMetricMap, trackerToConditionMap,
+                                  lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -357,12 +369,16 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                   periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                   allAtomMatchers, allConditionTrackers, allMetricProducers,
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                  trackerToMetricMap, trackerToConditionMap,
+                                  lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -380,12 +396,16 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                   periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                   allAtomMatchers, allConditionTrackers, allMetricProducers,
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                  trackerToMetricMap, trackerToConditionMap,
+                                  lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -403,13 +423,17 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                   periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                   allAtomMatchers, allConditionTrackers, allMetricProducers,
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                  trackerToMetricMap, trackerToConditionMap,
+                                  lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -427,13 +451,17 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> lifeSpanEventTrackerToMetricMap;
+    vector<int> metricsWithLifeSpan;
     std::set<int64_t> noReportMetricIds;
 
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
                                   periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
                                   allAtomMatchers, allConditionTrackers, allMetricProducers,
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap, noReportMetricIds));
+                                  trackerToMetricMap, trackerToConditionMap,
+                                  lifeSpanEventTrackerToMetricMap, metricsWithLifeSpan,
+                                  noReportMetricIds));
 }
 
 #else
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 5729feb..d7b9c11 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -200,8 +200,8 @@
     EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_deci_celsius(), 0);
 }
 
-TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) {
-    auto config = CreateStatsdConfig(GaugeMetric::ALL_CONDITION_CHANGES);
+TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents) {
+    auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
     int64_t baseTimeNs = 10 * NS_PER_SEC;
     int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
     int64_t bucketSizeNs =
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
new file mode 100644
index 0000000..0f13a4a
--- /dev/null
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -0,0 +1,242 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig() {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+    auto crashMatcher = CreateProcessCrashAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto saverModeMatcher = CreateBatterySaverModeStartAtomMatcher();
+
+    *config.add_atom_matcher() = saverModeMatcher;
+    *config.add_atom_matcher() = crashMatcher;
+    *config.add_atom_matcher() = screenOnMatcher;
+
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(crashMatcher.id());
+    countMetric->set_bucket(FIVE_MINUTES);
+    countMetric->mutable_dimensions_in_what()->set_field(
+        android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    countMetric->mutable_dimensions_in_what()->add_child()->set_field(1);  // uid field
+
+    auto metric_activation1 = config.add_metric_activation();
+    metric_activation1->set_metric_id(metricId);
+    auto event_activation1 = metric_activation1->add_event_activation();
+    event_activation1->set_atom_matcher_id(saverModeMatcher.id());
+    event_activation1->set_ttl_seconds(60 * 6);  // 6 minutes
+    auto event_activation2 = metric_activation1->add_event_activation();
+    event_activation2->set_atom_matcher_id(screenOnMatcher.id());
+    event_activation2->set_ttl_seconds(60 * 2);  // 2 minutes
+
+    return config;
+}
+
+}  // namespace
+
+TEST(MetricActivationE2eTest, TestCountMetric) {
+    auto config = CreateStatsdConfig();
+
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    sp<MetricProducer> metricProducer =
+        processor->mMetricsManagers.begin()->second->mAllMetricProducers[0];
+    auto& eventActivationMap = metricProducer->mEventActivationMap;
+
+    EXPECT_FALSE(metricProducer->mIsActive);
+    // Two activations: one is triggered by battery saver mode (tracker index 0), the other is
+    // triggered by screen on event (tracker index 2).
+    EXPECT_EQ(eventActivationMap.size(), 2u);
+    EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
+    EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+    EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0].activation_ns, 0);
+    EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2].activation_ns, 0);
+    EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    std::unique_ptr<LogEvent> event;
+
+    event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
+    processor->OnLogEvent(event.get());
+    EXPECT_FALSE(metricProducer->mIsActive);
+
+    // Activated by battery save mode.
+    event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
+    processor->OnLogEvent(event.get());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2].activation_ns, 0);
+    EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // First processed event.
+    event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
+    processor->OnLogEvent(event.get());
+
+    // Activated by screen on event.
+    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                          bucketStartTimeNs + 20);
+    processor->OnLogEvent(event.get());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // 2nd processed event.
+    // The activation by screen_on event expires, but the one by battery save mode is still active.
+    event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+    processor->OnLogEvent(event.get());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0].state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // 3rd processed event.
+    event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+    processor->OnLogEvent(event.get());
+
+    // All activations expired.
+    event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+    processor->OnLogEvent(event.get());
+    EXPECT_FALSE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + 20);
+    EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    // Re-activate.
+    event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                          bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    processor->OnLogEvent(event.get());
+    EXPECT_TRUE(metricProducer->mIsActive);
+    EXPECT_EQ(eventActivationMap[0].state, ActivationState::kNotActive);
+    EXPECT_EQ(eventActivationMap[0].activation_ns, bucketStartTimeNs + 10);
+    EXPECT_EQ(eventActivationMap[0].ttl_ns, 60 * 6 * NS_PER_SEC);
+    EXPECT_EQ(eventActivationMap[2].state, ActivationState::kActive);
+    EXPECT_EQ(eventActivationMap[2].activation_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
+    EXPECT_EQ(eventActivationMap[2].ttl_ns, 60 * 2 * NS_PER_SEC);
+
+    event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+    processor->OnLogEvent(event.get());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, ADB_DUMP,
+                            &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStartEndTimestamp(&reports);
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size());
+
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(
+            reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    EXPECT_EQ(4, countMetrics.data_size());
+
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    // Partial bucket as metric is deactivated.
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 8,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(1 /* uid field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+    EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+              data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+              data.bucket_info(0).end_bucket_elapsed_nanos());
+
+}
+
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index bf58b9c..60bd4a7 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/matchers/SimpleLogMatchingTracker.h"
 #include "src/metrics/GaugeMetricProducer.h"
 #include "src/stats_log_util.h"
 #include "logd/LogEvent.h"
@@ -40,6 +41,8 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
+const int64_t atomMatcherId = 678;
+const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
@@ -61,11 +64,19 @@
     gaugeFieldMatcher->add_child()->set_field(3);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
                                       pullerManager);
 
@@ -86,6 +97,12 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -103,6 +120,7 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
@@ -178,7 +196,15 @@
     alert.set_num_buckets(100);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
 
@@ -246,6 +272,12 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -263,6 +295,7 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
@@ -315,6 +348,12 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
@@ -330,7 +369,8 @@
                 return true;
             }));
 
-    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, -1, tagId,
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -388,6 +428,12 @@
     dim->set_field(conditionTag);
     dim->add_child()->set_field(1);
 
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, _, _, _, _, _))
             .WillRepeatedly(
@@ -420,7 +466,8 @@
                 return true;
             }));
 
-    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, -1, tagId,
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
 
     gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
@@ -463,7 +510,15 @@
     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
@@ -542,6 +597,12 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
@@ -574,6 +635,7 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
@@ -632,6 +694,12 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
+        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
             .WillOnce(Invoke([](int tagId, int64_t timeNs,
@@ -667,6 +735,7 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
 
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index b380b03..dd00561 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -17,6 +17,7 @@
 #include <unistd.h>
 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
 #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
 #include "src/shell/ShellSubscriber.h"
 #include "tests/metrics/metrics_test_helper.h"
 
@@ -26,7 +27,10 @@
 using namespace android::os::statsd;
 using android::sp;
 using std::vector;
+using testing::_;
+using testing::Invoke;
 using testing::NaggyMock;
+using testing::StrictMock;
 
 #ifdef __ANDROID__
 
@@ -51,7 +55,10 @@
     }
 };
 
-TEST(ShellSubscriberTest, testPushedSubscription) {
+void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
+                  sp<MockStatsPullerManager> pullerManager,
+                  const vector<std::shared_ptr<LogEvent>>& pushedEvents,
+                  const ShellData& expectedData) {
     // set up 2 pipes for read/write config and data
     int fds_config[2];
     ASSERT_EQ(0, pipe(fds_config));
@@ -59,10 +66,6 @@
     int fds_data[2];
     ASSERT_EQ(0, pipe(fds_data));
 
-    // create a simple config to get screen events
-    ShellSubscription config;
-    config.add_pushed()->set_atom_id(29);
-
     size_t bufferSize = config.ByteSize();
 
     // write the config to pipe, first write size of the config
@@ -75,15 +78,9 @@
     write(fds_config[1], buffer.data(), bufferSize);
     close(fds_config[1]);
 
-    // create a shell subscriber.
-    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-    sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap);
+    sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
     sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
 
-    LogEvent event1(29, 1000);
-    event1.write(2);
-    event1.init();
-
     // mimic a binder thread that a shell subscriber runs on. it would block.
     std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
         shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver);
@@ -93,44 +90,127 @@
     // let the shell subscriber to receive the config from pipe.
     std::this_thread::sleep_for(100ms);
 
-    // send a log event that matches the config.
-    std::thread log_reader([&shellClient, &event1] { shellClient->onLogEvent(event1); });
-    log_reader.detach();
+    if (pushedEvents.size() > 0) {
+        // send a log event that matches the config.
+        std::thread log_reader([&shellClient, &pushedEvents] {
+            for (const auto& event : pushedEvents) {
+                shellClient->onLogEvent(*event);
+            }
+        });
 
-    if (log_reader.joinable()) {
-        log_reader.join();
+        log_reader.detach();
+
+        if (log_reader.joinable()) {
+            log_reader.join();
+        }
     }
 
     // wait for the data to be written.
     std::this_thread::sleep_for(100ms);
 
-    // this is the expected screen event atom.
-    Atom atom;
-    atom.mutable_screen_state_changed()->set_state(
-            ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-
-    int atom_size = atom.ByteSize();
+    int expected_data_size = expectedData.ByteSize();
 
     // now read from the pipe. firstly read the atom size.
     size_t dataSize = 0;
     EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
-    EXPECT_EQ(atom_size, (int)dataSize);
+    EXPECT_EQ(expected_data_size, (int)dataSize);
 
     // then read that much data which is the atom in proto binary format
     vector<uint8_t> dataBuffer(dataSize);
     EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
 
     // make sure the received bytes can be parsed to an atom
-    Atom receivedAtom;
+    ShellData receivedAtom;
     EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
 
     // serialze the expected atom to bytes. and compare. to make sure they are the same.
-    vector<uint8_t> atomBuffer(atom_size);
-    atom.SerializeToArray(&atomBuffer[0], atom_size);
+    vector<uint8_t> atomBuffer(expected_data_size);
+    expectedData.SerializeToArray(&atomBuffer[0], expected_data_size);
     EXPECT_EQ(atomBuffer, dataBuffer);
     close(fds_data[0]);
 }
 
+TEST(ShellSubscriberTest, testPushedSubscription) {
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    vector<std::shared_ptr<LogEvent>> pushedList;
+
+    std::shared_ptr<LogEvent> event1 =
+            std::make_shared<LogEvent>(29 /*screen_state_atom_id*/, 1000 /*timestamp*/);
+    event1->write(::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    event1->init();
+    pushedList.push_back(event1);
+
+    // create a simple config to get screen events
+    ShellSubscription config;
+    config.add_pushed()->set_atom_id(29);
+
+    // this is the expected screen event atom.
+    ShellData shellData;
+    shellData.add_atom()->mutable_screen_state_changed()->set_state(
+            ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+    runShellTest(config, uidMap, pullerManager, pushedList, shellData);
+}
+
+namespace {
+
+int kUid1 = 1000;
+int kUid2 = 2000;
+
+int kCpuTime1 = 100;
+int kCpuTime2 = 200;
+
+ShellData getExpectedShellData() {
+    ShellData shellData;
+    auto* atom1 = shellData.add_atom()->mutable_cpu_active_time();
+    atom1->set_uid(kUid1);
+    atom1->set_time_millis(kCpuTime1);
+
+    auto* atom2 = shellData.add_atom()->mutable_cpu_active_time();
+    atom2->set_uid(kUid2);
+    atom2->set_time_millis(kCpuTime2);
+
+    return shellData;
+}
+
+ShellSubscription getPulledConfig() {
+    ShellSubscription config;
+    auto* pull_config = config.add_pulled();
+    pull_config->mutable_matcher()->set_atom_id(10016);
+    pull_config->set_freq_millis(2000);
+    return config;
+}
+
+}  // namespace
+
+TEST(ShellSubscriberTest, testPulledSubscription) {
+    sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(10016, _, _))
+            .WillRepeatedly(
+                    Invoke([](int tagId, int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data) {
+                        data->clear();
+                        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, timeNs);
+                        event->write(kUid1);
+                        event->write(kCpuTime1);
+                        event->init();
+                        data->push_back(event);
+                        // another event
+                        event = make_shared<LogEvent>(tagId, timeNs);
+                        event->write(kUid2);
+                        event->write(kCpuTime2);
+                        event->init();
+                        data->push_back(event);
+                        return true;
+                    }));
+
+    runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
+                 getExpectedShellData());
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tools/statsd-testdrive/Android.bp b/cmds/statsd/tools/statsd-testdrive/Android.bp
new file mode 100644
index 0000000..f566bc7
--- /dev/null
+++ b/cmds/statsd/tools/statsd-testdrive/Android.bp
@@ -0,0 +1,11 @@
+java_binary_host {
+    name: "statsd_testdrive",
+    manifest: "manifest.txt",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "platformprotos",
+        "guava",
+    ],
+}
diff --git a/cmds/statsd/tools/statsd-testdrive/manifest.txt b/cmds/statsd/tools/statsd-testdrive/manifest.txt
new file mode 100644
index 0000000..0266d11
--- /dev/null
+++ b/cmds/statsd/tools/statsd-testdrive/manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.statsd.testdrive.TestDrive
diff --git a/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java b/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
new file mode 100644
index 0000000..cc4e386
--- /dev/null
+++ b/cmds/statsd/tools/statsd-testdrive/src/com/android/statsd/testdrive/TestDrive.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.testdrive;
+
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+
+import com.google.common.io.Files;
+import com.google.protobuf.TextFormat;
+import com.google.protobuf.TextFormat.ParseException;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Formatter;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+public class TestDrive {
+
+    public static final int PULL_ATOM_START = 10000;
+    public static final long ATOM_MATCHER_ID = 1234567;
+
+    public static final String UPDATE_CONFIG_CMD = "cmd stats config update";
+    public static final String DUMP_REPORT_CMD = "cmd stats dump-report";
+    public static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
+    public static final String CONFIG_UID = "2000"; // shell uid
+    public static final long CONFIG_ID = 54321;
+
+    private static boolean mIsPushedAtom = false;
+
+    private static final Logger logger = Logger.getLogger(TestDrive.class.getName());
+
+    public static void main(String[] args) {
+        if (args.length != 1) {
+            logger.log(Level.SEVERE, "Usage: ./test_drive <atomId>");
+            return;
+        }
+        int atomId;
+        try {
+            atomId = Integer.valueOf(args[0]);
+        } catch (NumberFormatException e) {
+            logger.log(Level.SEVERE, "Bad atom id provided: " + args[0]);
+            return;
+        }
+        if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
+            logger.log(Level.SEVERE, "No such atom found: " + args[0]);
+            return;
+        }
+        mIsPushedAtom = atomId < PULL_ATOM_START;
+
+        TestDrive testDrive = new TestDrive();
+        TestDriveFormatter formatter = new TestDriveFormatter();
+        ConsoleHandler handler = new ConsoleHandler();
+        handler.setFormatter(formatter);
+        logger.addHandler(handler);
+        logger.setUseParentHandlers(false);
+
+        try {
+            StatsdConfig config = testDrive.createConfig(atomId);
+            if (config == null) {
+                logger.log(Level.SEVERE, "Failed to create valid config.");
+                return;
+            }
+            testDrive.pushConfig(config);
+            logger.info("Pushed the following config to statsd:");
+            logger.info(config.toString());
+            if (mIsPushedAtom) {
+                logger.info(
+                        "Now please play with the device to trigger the event. All events should "
+                                + "be dumped after 1 min ...");
+                Thread.sleep(60_000);
+            } else {
+                // wait for 2 min
+                logger.info("Now wait for 2 minutes ...");
+                Thread.sleep(120_000);
+            }
+            testDrive.dumpMetrics();
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Failed to test drive: " + e.getMessage());
+        } finally {
+            testDrive.removeConfig();
+        }
+    }
+
+    private void pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+        File configFile = File.createTempFile("statsdconfig", ".config");
+        configFile.deleteOnExit();
+        Files.write(config.toByteArray(), configFile);
+        String remotePath = "/data/local/tmp/" + configFile.getName();
+        runCommand(null, "adb", "push", configFile.getAbsolutePath(), remotePath);
+        runCommand(
+                null, "adb", "shell", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
+                String.valueOf(CONFIG_ID));
+    }
+
+    private void removeConfig() {
+        try {
+            runCommand(null, "adb", "shell", REMOVE_CONFIG_CMD, String.valueOf(CONFIG_ID));
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
+        }
+    }
+
+    // Runs a shell command. Output should go to outputFile. Returns error string.
+    private String runCommand(File outputFile, String... commands)
+            throws IOException, InterruptedException {
+        // Run macro on target
+        ProcessBuilder pb = new ProcessBuilder(commands);
+        // pb.redirectErrorStream(true);
+
+        if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
+            pb.redirectOutput(outputFile);
+        }
+        Process process = pb.start();
+
+        // capture any errors
+        StringBuilder out = new StringBuilder();
+        // Read output
+        BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+        String line = null, previous = null;
+        while ((line = br.readLine()) != null) {
+            if (!line.equals(previous)) {
+                previous = line;
+                out.append(line).append('\n');
+                logger.fine(line);
+            }
+        }
+
+        // Check result
+        if (process.waitFor() == 0) {
+            logger.fine("Success!");
+        } else {
+            // Abnormal termination: Log command parameters and output and throw ExecutionException
+            logger.log(Level.SEVERE, out.toString());
+        }
+        return out.toString();
+    }
+
+    private StatsdConfig createConfig(int atomId) {
+        try {
+            if (mIsPushedAtom) {
+                return createSimpleEventMetricConfig(atomId);
+            } else {
+                return createSimpleGaugeMetricConfig(atomId);
+            }
+        } catch (ParseException e) {
+            logger.log(
+                    Level.SEVERE,
+                    "Failed to parse the config! line: "
+                            + e.getLine()
+                            + " col: "
+                            + e.getColumn()
+                            + " "
+                            + e.getMessage());
+        }
+        return null;
+    }
+
+    private StatsdConfig createSimpleEventMetricConfig(int atomId) throws ParseException {
+        StatsdConfig.Builder baseBuilder = getSimpleEventMetricBaseConfig();
+        baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
+        return baseBuilder.build();
+    }
+
+    private StatsdConfig createSimpleGaugeMetricConfig(int atomId) throws ParseException {
+        StatsdConfig.Builder baseBuilder = getSimpleGaugeMetricBaseConfig();
+        baseBuilder.addAtomMatcher(createAtomMatcher(atomId));
+        return baseBuilder.build();
+    }
+
+    private AtomMatcher createAtomMatcher(int atomId) {
+        AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
+        atomMatcherBuilder
+                .setId(ATOM_MATCHER_ID)
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
+        return atomMatcherBuilder.build();
+    }
+
+    private StatsdConfig.Builder getSimpleEventMetricBaseConfig() throws ParseException {
+        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+        TextFormat.merge(EVENT_BASE_CONFIG_SRTR, builder);
+        return builder;
+    }
+
+    private StatsdConfig.Builder getSimpleGaugeMetricBaseConfig() throws ParseException {
+        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
+        TextFormat.merge(GAUGE_BASE_CONFIG_STR, builder);
+        return builder;
+    }
+
+    private ConfigMetricsReportList getReportList() throws Exception {
+        try {
+            File outputFile = File.createTempFile("statsdret", ".bin");
+            outputFile.deleteOnExit();
+            runCommand(
+                    outputFile,
+                    "adb",
+                    "shell",
+                    DUMP_REPORT_CMD,
+                    String.valueOf(CONFIG_ID),
+                    "--include_current_bucket",
+                    "--proto");
+            ConfigMetricsReportList reportList =
+                    ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
+            return reportList;
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+            logger.log(
+                    Level.SEVERE,
+                    "Failed to fetch and parse the statsd output report. "
+                            + "Perhaps there is not a valid statsd config for the requested "
+                            + "uid="
+                            + CONFIG_UID
+                            + ", id="
+                            + CONFIG_ID
+                            + ".");
+            throw (e);
+        }
+    }
+
+    private void dumpMetrics() throws Exception {
+        ConfigMetricsReportList reportList = getReportList();
+        // We may get multiple reports. Take the last one.
+        ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+        // Really should be only one metric.
+        if (report.getMetricsCount() != 1) {
+            logger.log(Level.SEVERE,
+                    "Only one report metric expected, got " + report.getMetricsCount());
+            return;
+        }
+
+        logger.info("Got following metric data dump:");
+        logger.info(report.getMetrics(0).toString());
+    }
+
+    private static final String EVENT_BASE_CONFIG_SRTR =
+            "id: 12345\n"
+                    + "event_metric {\n"
+                    + "  id: 1111\n"
+                    + "  what: 1234567\n"
+                    + "}\n"
+                    + "allowed_log_source: \"AID_GRAPHICS\"\n"
+                    + "allowed_log_source: \"AID_INCIDENTD\"\n"
+                    + "allowed_log_source: \"AID_STATSD\"\n"
+                    + "allowed_log_source: \"AID_RADIO\"\n"
+                    + "allowed_log_source: \"com.android.systemui\"\n"
+                    + "allowed_log_source: \"com.android.vending\"\n"
+                    + "allowed_log_source: \"AID_SYSTEM\"\n"
+                    + "allowed_log_source: \"AID_ROOT\"\n"
+                    + "allowed_log_source: \"AID_BLUETOOTH\"\n"
+                    + "\n"
+                    + "hash_strings_in_metric_report: false";
+
+    private static final String GAUGE_BASE_CONFIG_STR =
+            "id: 56789\n"
+                    + "gauge_metric {\n"
+                    + "  id: 2222\n"
+                    + "  what: 1234567\n"
+                    + "  gauge_fields_filter {\n"
+                    + "    include_all: true\n"
+                    + "  }\n"
+                    + "  bucket: ONE_MINUTE\n"
+                    + "}\n"
+                    + "allowed_log_source: \"AID_GRAPHICS\"\n"
+                    + "allowed_log_source: \"AID_INCIDENTD\"\n"
+                    + "allowed_log_source: \"AID_STATSD\"\n"
+                    + "allowed_log_source: \"AID_RADIO\"\n"
+                    + "allowed_log_source: \"com.android.systemui\"\n"
+                    + "allowed_log_source: \"com.android.vending\"\n"
+                    + "allowed_log_source: \"AID_SYSTEM\"\n"
+                    + "allowed_log_source: \"AID_ROOT\"\n"
+                    + "allowed_log_source: \"AID_BLUETOOTH\"\n"
+                    + "\n"
+                    + "hash_strings_in_metric_report: false";
+
+    public static class TestDriveFormatter extends Formatter {
+        public String format(LogRecord record) {
+            return record.getMessage() + "\n";
+        }
+    }
+}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index be9ccec..2d51038 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -6414,6 +6414,38 @@
 HPLandroid/util/proto/EncodedBuffer;->writeFromThisBuffer(II)V
 HPLandroid/util/proto/EncodedBuffer;->writeRawBuffer([BII)V
 HPLandroid/util/proto/EncodedBuffer;->writeRawFixed64(J)V
+HPLandroid/util/proto/ProtoInputStream;-><init>(Ljava/io/InputStream;I)V
+HPLandroid/util/proto/ProtoInputStream;-><init>(Ljava/io/InputStream;)V
+HPLandroid/util/proto/ProtoInputStream;-><init>([B)V
+HPLandroid/util/proto/ProtoInputStream;->getFieldNumber()I
+HPLandroid/util/proto/ProtoInputStream;->getWireType()I
+HPLandroid/util/proto/ProtoInputStream;->getOffset()I
+HPLandroid/util/proto/ProtoInputStream;->nextField()I
+HPLandroid/util/proto/ProtoInputStream;->isNextField(J)Z
+HPLandroid/util/proto/ProtoInputStream;->readDouble(J)D
+HPLandroid/util/proto/ProtoInputStream;->readFloat(J)F
+HPLandroid/util/proto/ProtoInputStream;->readInt(J)I
+HPLandroid/util/proto/ProtoInputStream;->readLong(J)J
+HPLandroid/util/proto/ProtoInputStream;->readBoolean(J)Z
+HPLandroid/util/proto/ProtoInputStream;->readString(J)Ljava/lang/String;
+HPLandroid/util/proto/ProtoInputStream;->readBytes(J)[B
+HPLandroid/util/proto/ProtoInputStream;->start(J)J
+HPLandroid/util/proto/ProtoInputStream;->end(J)V
+HPLandroid/util/proto/ProtoInputStream;->readTag()V
+HPLandroid/util/proto/ProtoInputStream;->decodeZigZag32(I)I
+HPLandroid/util/proto/ProtoInputStream;->decodeZigZag64(J)J
+HPLandroid/util/proto/ProtoInputStream;->readVarint()J
+HPLandroid/util/proto/ProtoInputStream;->readFixed32()I
+HPLandroid/util/proto/ProtoInputStream;->readFixed64()J
+HPLandroid/util/proto/ProtoInputStream;->readRawBytes(I)[B
+HPLandroid/util/proto/ProtoInputStream;->readRawString(I)Ljava/lang/String;
+HPLandroid/util/proto/ProtoInputStream;->fillBuffer()V
+HPLandroid/util/proto/ProtoInputStream;->skip()V
+HPLandroid/util/proto/ProtoInputStream;->incOffset(I)V
+HPLandroid/util/proto/ProtoInputStream;->checkPacked(J)V
+HPLandroid/util/proto/ProtoInputStream;->assertFieldNumber(J)V
+HPLandroid/util/proto/ProtoInputStream;->assertWireType(I)V
+HPLandroid/util/proto/ProtoInputStream;->assertFreshData()V
 HPLandroid/util/proto/ProtoOutputStream;-><init>(Ljava/io/FileDescriptor;)V
 HPLandroid/util/proto/ProtoOutputStream;-><init>(Ljava/io/OutputStream;)V
 HPLandroid/util/proto/ProtoOutputStream;->compactIfNecessary()V
@@ -58580,6 +58612,7 @@
 Landroid/util/apk/ZipUtils;
 Landroid/util/jar/StrictJarFile;
 Landroid/util/proto/EncodedBuffer;
+Landroid/util/proto/ProtoInputStream;
 Landroid/util/proto/ProtoOutputStream;
 Landroid/view/-$$Lambda$FocusFinder$FocusSorter$h0f2ZYL6peSaaEeCCkAoYs_YZvU;
 Landroid/view/-$$Lambda$FocusFinder$FocusSorter$kW7K1t9q7Y62V38r-7g6xRzqqq8;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index b71274c..bbc3f35 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1383,62 +1383,6 @@
 Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V
 Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService;
 Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V
-Landroid/system/Int32Ref;->value:I
-Landroid/system/OsConstants;-><init>()V
-Landroid/system/OsConstants;->AF_NETLINK:I
-Landroid/system/OsConstants;->AF_PACKET:I
-Landroid/system/OsConstants;->ARPHRD_ETHER:I
-Landroid/system/OsConstants;->ARPHRD_LOOPBACK:I
-Landroid/system/OsConstants;->CAP_TO_INDEX(I)I
-Landroid/system/OsConstants;->CAP_TO_MASK(I)I
-Landroid/system/OsConstants;->ENONET:I
-Landroid/system/OsConstants;->ETH_P_ALL:I
-Landroid/system/OsConstants;->ETH_P_ARP:I
-Landroid/system/OsConstants;->ETH_P_IP:I
-Landroid/system/OsConstants;->ETH_P_IPV6:I
-Landroid/system/OsConstants;->EUSERS:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REPLY:I
-Landroid/system/OsConstants;->ICMP6_ECHO_REQUEST:I
-Landroid/system/OsConstants;->ICMP_ECHO:I
-Landroid/system/OsConstants;->ICMP_ECHOREPLY:I
-Landroid/system/OsConstants;->initConstants()V
-Landroid/system/OsConstants;->IP_MULTICAST_ALL:I
-Landroid/system/OsConstants;->IP_RECVTOS:I
-Landroid/system/OsConstants;->MAP_POPULATE:I
-Landroid/system/OsConstants;->NETLINK_NETFILTER:I
-Landroid/system/OsConstants;->NETLINK_ROUTE:I
-Landroid/system/OsConstants;->O_DIRECT:I
-Landroid/system/OsConstants;->placeholder()I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT:I
-Landroid/system/OsConstants;->PR_CAP_AMBIENT_RAISE:I
-Landroid/system/OsConstants;->RLIMIT_NOFILE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV4_RULE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFADDR:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_IFINFO:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_MROUTE:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_PREFIX:I
-Landroid/system/OsConstants;->RTMGRP_IPV6_ROUTE:I
-Landroid/system/OsConstants;->RTMGRP_LINK:I
-Landroid/system/OsConstants;->RTMGRP_NEIGH:I
-Landroid/system/OsConstants;->RTMGRP_NOTIFY:I
-Landroid/system/OsConstants;->RTMGRP_TC:I
-Landroid/system/OsConstants;->SO_DOMAIN:I
-Landroid/system/OsConstants;->SO_PROTOCOL:I
-Landroid/system/OsConstants;->SPLICE_F_MORE:I
-Landroid/system/OsConstants;->SPLICE_F_MOVE:I
-Landroid/system/OsConstants;->SPLICE_F_NONBLOCK:I
-Landroid/system/OsConstants;->TIOCOUTQ:I
-Landroid/system/OsConstants;->UDP_ENCAP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP:I
-Landroid/system/OsConstants;->UDP_ENCAP_ESPINUDP_NON_IKE:I
-Landroid/system/OsConstants;->UNIX_PATH_MAX:I
-Landroid/system/OsConstants;->XATTR_CREATE:I
-Landroid/system/OsConstants;->XATTR_REPLACE:I
-Landroid/system/OsConstants;->_LINUX_CAPABILITY_VERSION_3:I
-Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
 Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
 Landroid/telephony/TelephonyManager$MultiSimVariants;->values()[Landroid/telephony/TelephonyManager$MultiSimVariants;
 Landroid/util/Singleton;-><init>()V
@@ -2339,93 +2283,6 @@
 Lcom/google/android/mms/util/PduCache;->purgeAll()V
 Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
 Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Ldalvik/system/BaseDexClassLoader;-><init>(Ljava/lang/String;Ljava/io/File;Ljava/lang/String;Ljava/lang/ClassLoader;Z)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V
-Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V
-Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
-Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;-><init>(IILjava/lang/String;)V
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mMessage:Ljava/lang/String;
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyState:I
-Ldalvik/system/BlockGuard$BlockGuardPolicyException;->mPolicyViolated:I
-Ldalvik/system/BlockGuard$Policy;->onNetwork()V
-Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V
-Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->LAX_POLICY:Ldalvik/system/BlockGuard$Policy;
-Ldalvik/system/BlockGuard;->setThreadPolicy(Ldalvik/system/BlockGuard$Policy;)V
-Ldalvik/system/BlockGuard;->threadPolicy:Ljava/lang/ThreadLocal;
-Ldalvik/system/CloseGuard$DefaultReporter;-><init>()V
-Ldalvik/system/CloseGuard$Reporter;->report(Ljava/lang/String;Ljava/lang/Throwable;)V
-Ldalvik/system/CloseGuard;-><init>()V
-Ldalvik/system/CloseGuard;->close()V
-Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
-Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
-Ldalvik/system/CloseGuard;->setEnabled(Z)V
-Ldalvik/system/CloseGuard;->setReporter(Ldalvik/system/CloseGuard$Reporter;)V
-Ldalvik/system/CloseGuard;->warnIfOpen()V
-Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String;
-Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
-Ldalvik/system/DexFile;->isBackedByOatFile()Z
-Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
-Ldalvik/system/DexFile;->loadDex(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexFile;->mCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->mFileName:Ljava/lang/String;
-Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexFile;->openDexFileNative(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
-Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V
-Ldalvik/system/DexPathList$Element;-><init>(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V
-Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList$Element;->path:Ljava/io/File;
-Ldalvik/system/DexPathList$NativeLibraryElement;-><init>(Ljava/io/File;)V
-Ldalvik/system/DexPathList$NativeLibraryElement;->path:Ljava/io/File;
-Ldalvik/system/DexPathList;-><init>(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V
-Ldalvik/system/DexPathList;->addNativePath(Ljava/util/Collection;)V
-Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader;
-Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->dexElementsSuppressedExceptions:[Ljava/io/IOException;
-Ldalvik/system/DexPathList;->loadDexFile(Ljava/io/File;Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ldalvik/system/DexFile;
-Ldalvik/system/DexPathList;->makeDexElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;Ljava/lang/ClassLoader;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makeInMemoryDexElements([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;)[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->makePathElements(Ljava/util/List;Ljava/io/File;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
-Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement;
-Ldalvik/system/DexPathList;->splitPaths(Ljava/lang/String;Z)Ljava/util/List;
-Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List;
-Ldalvik/system/SocketTagger;->get()Ldalvik/system/SocketTagger;
-Ldalvik/system/SocketTagger;->tag(Ljava/net/Socket;)V
-Ldalvik/system/SocketTagger;->untag(Ljava/net/Socket;)V
-Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V
-Ldalvik/system/VMDebug;->dumpReferenceTables()V
-Ldalvik/system/VMDebug;->isDebuggerConnected()Z
-Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
-Ldalvik/system/VMRuntime;->clearGrowthLimit()V
-Ldalvik/system/VMRuntime;->gcSoftReferences()V
-Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getExternalBytesAllocated()J
-Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
-Ldalvik/system/VMRuntime;->getMinimumHeapSize()J
-Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
-Ldalvik/system/VMRuntime;->is64Bit()Z
-Ldalvik/system/VMRuntime;->is64BitAbi(Ljava/lang/String;)Z
-Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
-Ldalvik/system/VMRuntime;->registerNativeAllocation(I)V
-Ldalvik/system/VMRuntime;->registerNativeFree(I)V
-Ldalvik/system/VMRuntime;->runFinalization(J)V
-Ldalvik/system/VMRuntime;->runFinalizationSync()V
-Ldalvik/system/VMRuntime;->setMinimumHeapSize(J)J
-Ldalvik/system/VMRuntime;->setTargetHeapUtilization(F)F
-Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
-Ldalvik/system/VMRuntime;->trackExternalAllocation(J)Z
-Ldalvik/system/VMRuntime;->trackExternalFree(J)V
-Ldalvik/system/VMRuntime;->vmInstructionSet()Ljava/lang/String;
-Ldalvik/system/VMRuntime;->vmLibrary()Ljava/lang/String;
-Ldalvik/system/VMStack;->fillStackTraceElements(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I
-Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
-Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
-Ldalvik/system/VMStack;->getThreadStackTrace(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;
 Ljava/io/Console;->encoding()Ljava/lang/String;
 Ljava/io/File;->filePath:Ljava/nio/file/Path;
 Ljava/io/File;->fs:Ljava/io/FileSystem;
@@ -2501,18 +2358,6 @@
 Ljava/lang/Class;->objectSize:I
 Ljava/lang/Class;->status:I
 Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
-Ljava/lang/Daemons$Daemon;->isRunning()Z
-Ljava/lang/Daemons$Daemon;->start()V
-Ljava/lang/Daemons$Daemon;->stop()V
-Ljava/lang/Daemons$Daemon;->thread:Ljava/lang/Thread;
-Ljava/lang/Daemons$FinalizerDaemon;->finalizingObject:Ljava/lang/Object;
-Ljava/lang/Daemons$FinalizerDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerDaemon;
-Ljava/lang/Daemons$FinalizerWatchdogDaemon;->INSTANCE:Ljava/lang/Daemons$FinalizerWatchdogDaemon;
-Ljava/lang/Daemons$ReferenceQueueDaemon;->INSTANCE:Ljava/lang/Daemons$ReferenceQueueDaemon;
-Ljava/lang/Daemons;->MAX_FINALIZE_NANOS:J
-Ljava/lang/Daemons;->requestHeapTrim()V
-Ljava/lang/Daemons;->start()V
-Ljava/lang/Daemons;->stop()V
 Ljava/lang/Double;->value:D
 Ljava/lang/Enum;->getSharedConstants(Ljava/lang/Class;)[Ljava/lang/Enum;
 Ljava/lang/Enum;->name:Ljava/lang/String;
@@ -2522,11 +2367,6 @@
 Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V
 Ljava/lang/Long;->value:J
 Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I
-Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
-Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
-Ljava/lang/ref/FinalizerReference;->queue:Ljava/lang/ref/ReferenceQueue;
-Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V
 Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
 Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
 Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
@@ -2669,12 +2509,6 @@
 Ljava/nio/charset/Charset;->defaultCharset:Ljava/nio/charset/Charset;
 Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
 Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
-Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
-Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
-Ljava/nio/NioUtils;->freeDirectBuffer(Ljava/nio/ByteBuffer;)V
-Ljava/nio/NioUtils;->unsafeArray(Ljava/nio/ByteBuffer;)[B
-Ljava/nio/NioUtils;->unsafeArrayOffset(Ljava/nio/ByteBuffer;)I
 Ljava/security/KeyPairGenerator;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/KeyPairGenerator;
 Ljava/security/KeyStore;->keyStoreSpi:Ljava/security/KeyStoreSpi;
 Ljava/security/Signature;->getInstance(Lsun/security/jca/GetInstance$Instance;Ljava/lang/String;)Ljava/security/Signature;
@@ -2790,19 +2624,6 @@
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
-Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/io/Libcore;->os:Llibcore/io/Os;
-Llibcore/io/Memory;->peekByte(J)B
-Llibcore/io/Memory;->peekByteArray(J[BII)V
-Llibcore/io/Memory;->peekInt(JZ)I
-Llibcore/io/Memory;->peekLong(JZ)J
-Llibcore/io/Memory;->pokeByte(JB)V
-Llibcore/io/Memory;->pokeByteArray(J[BII)V
-Llibcore/io/Memory;->pokeInt(JIZ)V
-Llibcore/io/Memory;->pokeLong(JJZ)V
-Llibcore/io/Streams;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)I
-Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
-Llibcore/util/ZoneInfo;->mTransitions:[J
 Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I
 Lorg/ccil/cowan/tagsoup/ElementType;->theAtts:Lorg/ccil/cowan/tagsoup/AttributesImpl;
@@ -2821,100 +2642,6 @@
 Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/Schema;->theRoot:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/Schema;->theURI:Ljava/lang/String;
-Lorg/json/JSONArray;->values:Ljava/util/List;
-Lorg/json/JSONArray;->writeTo(Lorg/json/JSONStringer;)V
-Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
-Lorg/json/JSONObject;->checkName(Ljava/lang/String;)Ljava/lang/String;
-Lorg/json/JSONObject;->keySet()Ljava/util/Set;
-Lorg/json/JSONObject;->nameValuePairs:Ljava/util/LinkedHashMap;
-Lorg/json/JSONObject;->NEGATIVE_ZERO:Ljava/lang/Double;
-Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
-Lorg/json/JSONStringer;-><init>(I)V
-Lorg/json/JSONStringer;->beforeKey()V
-Lorg/json/JSONStringer;->beforeValue()V
-Lorg/json/JSONStringer;->close(Lorg/json/JSONStringer$Scope;Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
-Lorg/json/JSONStringer;->indent:Ljava/lang/String;
-Lorg/json/JSONStringer;->newline()V
-Lorg/json/JSONStringer;->open(Lorg/json/JSONStringer$Scope;Ljava/lang/String;)Lorg/json/JSONStringer;
-Lorg/json/JSONStringer;->out:Ljava/lang/StringBuilder;
-Lorg/json/JSONStringer;->peek()Lorg/json/JSONStringer$Scope;
-Lorg/json/JSONStringer;->replaceTop(Lorg/json/JSONStringer$Scope;)V
-Lorg/json/JSONStringer;->stack:Ljava/util/List;
-Lorg/json/JSONStringer;->string(Ljava/lang/String;)V
-Lorg/json/JSONTokener;->in:Ljava/lang/String;
-Lorg/json/JSONTokener;->nextCleanInternal()I
-Lorg/json/JSONTokener;->nextToInternal(Ljava/lang/String;)Ljava/lang/String;
-Lorg/json/JSONTokener;->pos:I
-Lorg/json/JSONTokener;->readArray()Lorg/json/JSONArray;
-Lorg/json/JSONTokener;->readEscapeCharacter()C
-Lorg/json/JSONTokener;->readLiteral()Ljava/lang/Object;
-Lorg/json/JSONTokener;->readObject()Lorg/json/JSONObject;
-Lorg/json/JSONTokener;->skipToEndOfLine()V
-Lorg/w3c/dom/ls/LSSerializerFilter;->getWhatToShow()I
-Lorg/w3c/dom/traversal/NodeFilter;->acceptNode(Lorg/w3c/dom/Node;)S
-Lorg/w3c/dom/traversal/NodeIterator;->detach()V
-Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node;
-Lorg/xml/sax/ext/Attributes2Impl;->declared:[Z
-Lorg/xml/sax/ext/Attributes2Impl;->specified:[Z
-Lorg/xml/sax/ext/Locator2Impl;->encoding:Ljava/lang/String;
-Lorg/xml/sax/ext/Locator2Impl;->version:Ljava/lang/String;
-Lorg/xml/sax/helpers/AttributesImpl;->badIndex(I)V
-Lorg/xml/sax/helpers/AttributesImpl;->data:[Ljava/lang/String;
-Lorg/xml/sax/helpers/AttributesImpl;->ensureCapacity(I)V
-Lorg/xml/sax/helpers/AttributesImpl;->length:I
-Lorg/xml/sax/helpers/LocatorImpl;->columnNumber:I
-Lorg/xml/sax/helpers/LocatorImpl;->lineNumber:I
-Lorg/xml/sax/helpers/LocatorImpl;->publicId:Ljava/lang/String;
-Lorg/xml/sax/helpers/LocatorImpl;->systemId:Ljava/lang/String;
-Lorg/xml/sax/helpers/NamespaceSupport;->contextPos:I
-Lorg/xml/sax/helpers/NamespaceSupport;->contexts:[Lorg/xml/sax/helpers/NamespaceSupport$Context;
-Lorg/xml/sax/helpers/NamespaceSupport;->currentContext:Lorg/xml/sax/helpers/NamespaceSupport$Context;
-Lorg/xml/sax/helpers/NamespaceSupport;->EMPTY_ENUMERATION:Ljava/util/Enumeration;
-Lorg/xml/sax/helpers/NamespaceSupport;->namespaceDeclUris:Z
-Lorg/xml/sax/helpers/ParserAdapter;->attAdapter:Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;
-Lorg/xml/sax/helpers/ParserAdapter;->atts:Lorg/xml/sax/helpers/AttributesImpl;
-Lorg/xml/sax/helpers/ParserAdapter;->checkNotParsing(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/ParserAdapter;->contentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->dtdHandler:Lorg/xml/sax/DTDHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->entityResolver:Lorg/xml/sax/EntityResolver;
-Lorg/xml/sax/helpers/ParserAdapter;->errorHandler:Lorg/xml/sax/ErrorHandler;
-Lorg/xml/sax/helpers/ParserAdapter;->locator:Lorg/xml/sax/Locator;
-Lorg/xml/sax/helpers/ParserAdapter;->makeException(Ljava/lang/String;)Lorg/xml/sax/SAXParseException;
-Lorg/xml/sax/helpers/ParserAdapter;->nameParts:[Ljava/lang/String;
-Lorg/xml/sax/helpers/ParserAdapter;->namespaces:Z
-Lorg/xml/sax/helpers/ParserAdapter;->nsSupport:Lorg/xml/sax/helpers/NamespaceSupport;
-Lorg/xml/sax/helpers/ParserAdapter;->parser:Lorg/xml/sax/Parser;
-Lorg/xml/sax/helpers/ParserAdapter;->parsing:Z
-Lorg/xml/sax/helpers/ParserAdapter;->prefixes:Z
-Lorg/xml/sax/helpers/ParserAdapter;->processName(Ljava/lang/String;ZZ)[Ljava/lang/String;
-Lorg/xml/sax/helpers/ParserAdapter;->reportError(Ljava/lang/String;)V
-Lorg/xml/sax/helpers/ParserAdapter;->setup(Lorg/xml/sax/Parser;)V
-Lorg/xml/sax/helpers/ParserAdapter;->setupParser()V
-Lorg/xml/sax/helpers/ParserAdapter;->uris:Z
-Lorg/xml/sax/helpers/XMLFilterImpl;->contentHandler:Lorg/xml/sax/ContentHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->dtdHandler:Lorg/xml/sax/DTDHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->entityResolver:Lorg/xml/sax/EntityResolver;
-Lorg/xml/sax/helpers/XMLFilterImpl;->errorHandler:Lorg/xml/sax/ErrorHandler;
-Lorg/xml/sax/helpers/XMLFilterImpl;->locator:Lorg/xml/sax/Locator;
-Lorg/xml/sax/helpers/XMLFilterImpl;->parent:Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/helpers/XMLFilterImpl;->setupParse()V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->documentHandler:Lorg/xml/sax/DocumentHandler;
-Lorg/xml/sax/helpers/XMLReaderAdapter;->qAtts:Lorg/xml/sax/helpers/XMLReaderAdapter$AttributesAdapter;
-Lorg/xml/sax/helpers/XMLReaderAdapter;->setup(Lorg/xml/sax/XMLReader;)V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->setupXMLReader()V
-Lorg/xml/sax/helpers/XMLReaderAdapter;->xmlReader:Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/helpers/XMLReaderFactory;->loadClass(Ljava/lang/ClassLoader;Ljava/lang/String;)Lorg/xml/sax/XMLReader;
-Lorg/xml/sax/InputSource;->byteStream:Ljava/io/InputStream;
-Lorg/xml/sax/InputSource;->characterStream:Ljava/io/Reader;
-Lorg/xml/sax/InputSource;->encoding:Ljava/lang/String;
-Lorg/xml/sax/InputSource;->publicId:Ljava/lang/String;
-Lorg/xml/sax/InputSource;->systemId:Ljava/lang/String;
-Lorg/xml/sax/SAXException;->exception:Ljava/lang/Exception;
-Lorg/xml/sax/SAXParseException;->columnNumber:I
-Lorg/xml/sax/SAXParseException;->init(Ljava/lang/String;Ljava/lang/String;II)V
-Lorg/xml/sax/SAXParseException;->lineNumber:I
-Lorg/xml/sax/SAXParseException;->publicId:Ljava/lang/String;
-Lorg/xml/sax/SAXParseException;->systemId:Ljava/lang/String;
 Lsun/misc/Cleaner;->clean()V
 Lsun/misc/Unsafe;->addressSize()I
 Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-max-sdk-p-blacklist.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/hiddenapi-max-sdk-p-blacklist.txt
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 45e38cf..e5e64d3 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -122,14 +122,6 @@
 Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
 Landroid/os/UserManager;->isAdminUser()Z
 Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/system/NetlinkSocketAddress;-><init>(II)V
-Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
-Landroid/system/Os;->sendto(Ljava/io/FileDescriptor;[BIIILjava/net/SocketAddress;)I
-Landroid/system/Os;->setsockoptIfreq(Ljava/io/FileDescriptor;IILjava/lang/String;)V
-Landroid/system/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Landroid/system/PacketSocketAddress;-><init>(I[B)V
-Landroid/system/PacketSocketAddress;-><init>(SI)V
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
 Landroid/telephony/ims/compat/ImsService;-><init>()V
 Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 56ca98f..95bcf0e 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2354,6 +2354,7 @@
 android.nfc.NfcAdapter$CreateNdefMessageCallback
 android.nfc.NfcManager
 android.opengl.EGL14
+android.opengl.EGL15
 android.opengl.EGLConfig
 android.opengl.EGLContext
 android.opengl.EGLDisplay
@@ -3226,7 +3227,7 @@
 android.view.DisplayEventReceiver
 android.view.DisplayInfo
 android.view.DisplayInfo$1
-android.view.DisplayListCanvas
+android.graphics.RecordingCanvas
 android.view.FallbackEventHandler
 android.view.FocusFinder
 android.view.FocusFinder$1
@@ -3304,8 +3305,8 @@
 android.view.OrientationEventListener$SensorEventListenerImpl
 android.view.PointerIcon
 android.view.PointerIcon$1
-android.view.RenderNode
-android.view.RenderNode$NoImagePreloadHolder
+android.graphics.RenderNode
+android.graphics.RenderNode$NoImagePreloadHolder
 android.view.RenderNodeAnimator
 android.view.RenderNodeAnimator$1
 android.view.RenderNodeAnimatorSetHelper
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aca80b4..3cc5e37 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4678,7 +4678,7 @@
         if (decor != null) {
             decor.cancelPendingInputEvents();
         }
-        if (options != null && !isTopOfTask()) {
+        if (options != null) {
             mActivityTransitionState.startExitOutTransition(this, options);
         }
     }
@@ -4882,6 +4882,7 @@
             Bundle options)
             throws IntentSender.SendIntentException {
         try {
+            options = transferSpringboardActivityOptions(options);
             String resolvedType = null;
             if (fillInIntent != null) {
                 fillInIntent.migrateExtraStreamToClipData();
@@ -4898,6 +4899,12 @@
                 throw new IntentSender.SendIntentException();
             }
             Instrumentation.checkStartActivityResult(result, null);
+
+            if (options != null) {
+                // Only when the options are not null, as the intent can point to something other
+                // than an Activity.
+                cancelInputsAndStartExitTransition(options);
+            }
         } catch (RemoteException e) {
         }
         if (requestCode >= 0) {
@@ -6471,7 +6478,7 @@
      *
      * @return true if this is the topmost, non-finishing activity in its task.
      */
-    private boolean isTopOfTask() {
+    final boolean isTopOfTask() {
         if (mToken == null || mWindow == null) {
             return false;
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 14b8ae4..7330da3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -137,6 +137,17 @@
     private static final int FIRST_START_NON_FATAL_ERROR_CODE = 100;
     private static final int LAST_START_NON_FATAL_ERROR_CODE = 199;
 
+    /**
+     * Disable hidden API checks for the newly started instrumentation.
+     * @hide
+     */
+    public static final int INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+    /**
+     * Mount full external storage for the newly started instrumentation.
+     * @hide
+     */
+    public static final int INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL = 1 << 1;
+
     static final class UidObserver extends IUidObserver.Stub {
         final OnUidImportanceListener mListener;
         final Context mContext;
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 784ce04..069effd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -22,6 +22,7 @@
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
@@ -123,17 +124,6 @@
     public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
 
     /**
-     * Saves the current activity manager state and includes the saved state in the next dump of
-     * activity manager.
-     */
-    public abstract void saveANRState(String reason);
-
-    /**
-     * Clears the previously saved activity manager ANR state.
-     */
-    public abstract void clearSavedANRState();
-
-    /**
      * @return true if runtime was restarted, false if it's normal boot
      */
     public abstract boolean isRuntimeRestarted();
@@ -165,10 +155,19 @@
 
     /**
      * Returns a list that contains the memory stats for currently running processes.
+     *
+     * Only processes managed by ActivityManagerService are included.
      */
     public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
 
     /**
+     * Returns a list that contains the memory stats for monitored native processes.
+     *
+     * The list of the monitored processes is defined in MemoryStatUtil class.
+     */
+    public abstract List<ProcessMemoryState> getMemoryStateForNativeProcesses();
+
+    /**
      * Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
      * needed.
      */
@@ -187,9 +186,6 @@
     /** Trims memory usage in the system by removing/stopping unused application processes. */
     public abstract void trimApplications();
 
-    /** Closes all system dialogs. */
-    public abstract void closeSystemDialogs(String reason);
-
     /** Kill the processes in the list due to their tasks been removed. */
     public abstract void killProcessesForRemovedTask(ArrayList<Object> procsToKill);
 
@@ -242,4 +238,46 @@
             throws TransactionTooLargeException;
 
     public abstract void disconnectActivityFromServices(Object connectionHolder);
+    public abstract void cleanUpServices(int userId, ComponentName component, Intent baseIntent);
+    public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId);
+    public abstract void ensureBootCompleted();
+    public abstract void updateOomLevelsForDisplay(int displayId);
+    public abstract boolean isActivityStartsLoggingEnabled();
+    public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
+
+    /** Input dispatch timeout to a window, start the ANR process. */
+    public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
+    public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
+            ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
+            boolean aboveSystem, String reason);
+
+    /**
+     * Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
+     * flags.
+     */
+    public abstract void broadcastGlobalConfigurationChanged(int changes, boolean initLocale);
+
+    /**
+     * Sends {@link android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS} with all the appropriate
+     * flags.
+     */
+    public abstract void broadcastCloseSystemDialogs(String reason);
+
+    /**
+     * Kills all background processes, except those matching any of the specified properties.
+     *
+     * @param minTargetSdk the target SDK version at or above which to preserve processes,
+     *                     or {@code -1} to ignore the target SDK
+     * @param maxProcState the process state at or below which to preserve processes,
+     *                     or {@code -1} to ignore the process state
+     */
+    public abstract void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState);
+
+    /** Starts a given process. */
+    public abstract void startProcess(String processName, ApplicationInfo info,
+            boolean knownToBeDead, String hostingType, ComponentName hostingName);
+
+    /** Starts up the starting activity process for debugging if needed. */
+    public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+            ProfilerInfo profilerInfo);
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 3c9a2d4..94b42ff 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -195,6 +195,13 @@
     private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
 
     /**
+     * See {@link #setPendingIntentLaunchFlags(int)}
+     * @hide
+     */
+    private static final String KEY_PENDING_INTENT_LAUNCH_FLAGS =
+            "android.activity.pendingIntentLaunchFlags";
+
+    /**
      * See {@link #setTaskOverlay}.
      * @hide
      */
@@ -309,6 +316,7 @@
     @WindowConfiguration.ActivityType
     private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED;
     private int mLaunchTaskId = -1;
+    private int mPendingIntentLaunchFlags;
     private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     private boolean mLockTaskMode = false;
     private boolean mDisallowEnterPictureInPictureWhileLaunching;
@@ -932,6 +940,7 @@
         mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
         mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
+        mPendingIntentLaunchFlags = opts.getInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, 0);
         mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
         mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
         mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
@@ -1233,6 +1242,22 @@
     }
 
     /**
+     * Specifies intent flags to be applied for any activity started from a PendingIntent.
+     *
+     * @hide
+     */
+    public void setPendingIntentLaunchFlags(@android.content.Intent.Flags int flags) {
+        mPendingIntentLaunchFlags = flags;
+    }
+
+    /**
+     * @hide
+     */
+    public int getPendingIntentLaunchFlags() {
+        return mPendingIntentLaunchFlags;
+    }
+
+    /**
      * Set's whether the activity launched with this option should be a task overlay. That is the
      * activity will always be the top activity of the task.  If {@param canResume} is true, then
      * the task will also not be moved to the front of the stack.
@@ -1463,6 +1488,9 @@
         if (mLaunchTaskId != -1) {
             b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
         }
+        if (mPendingIntentLaunchFlags != 0) {
+            b.putInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, mPendingIntentLaunchFlags);
+        }
         if (mTaskOverlay) {
             b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
         }
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index af8aa4e..b8fe2f1 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -45,6 +45,12 @@
     public static final int INVALID_STACK_ID = -1;
 
     /**
+     * Invalid task ID.
+     * @hide
+     */
+    public static final int INVALID_TASK_ID = -1;
+
+    /**
      * Parameter to {@link IActivityTaskManager#setTaskWindowingModeSplitScreenPrimary} which
      * specifies the position of the created docked stack at the top half of the screen if
      * in portrait mode or at the left half of the screen if in landscape mode.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ff6aca6..4756bf4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1425,60 +1425,10 @@
             PrintWriter pw = new FastPrintWriter(
                     new FileOutputStream(pfd.getFileDescriptor()));
             PrintWriterPrinter printer = new PrintWriterPrinter(pw);
-            SQLiteDebug.dump(printer, args);
-
-            if (isSystem) {
-                dumpDatabaseFileSizes(pw, Environment.getDataSystemDirectory(), true);
-                dumpDatabaseFileSizes(pw, Environment.getDataSystemDeDirectory(), true);
-                dumpDatabaseFileSizes(pw, Environment.getDataSystemCeDirectory(), true);
-            } else {
-                Context context = getApplication();
-                if (context != null) {
-                    dumpDatabaseFileSizes(pw,
-                            getDatabasesDir(context.createDeviceProtectedStorageContext()),
-                            false);
-                    dumpDatabaseFileSizes(pw,
-                            getDatabasesDir(context.createCredentialProtectedStorageContext()),
-                            false);
-                }
-            }
+            SQLiteDebug.dump(printer, args, isSystem);
             pw.flush();
         }
 
-        private void dumpDatabaseFileSizes(PrintWriter pw, File dir, boolean isSystem) {
-            final File[] files = dir.listFiles();
-            if (files == null || files.length == 0) {
-                return;
-            }
-            Arrays.sort(files, (a, b) -> a.getName().compareTo(b.getName()));
-
-            boolean needHeader = true;
-            for (File f : files) {
-                if (isSystem) {
-                    // If it's the system server, the directory contains other files too, so
-                    // filter by file extensions.
-                    // (If it's an app, just print all files because they may not use *.db
-                    // extension.)
-                    final String name = f.getName();
-                    if (!(name.endsWith(".db") || name.endsWith(".db-wal")
-                            || name.endsWith(".db-journal"))) {
-                        continue;
-                    }
-                }
-                if (needHeader) {
-                    pw.println();
-                    pw.println("Database files in " + dir.getAbsolutePath() + ":");
-                    needHeader = false;
-                }
-
-                pw.print("  ");
-                pw.print(f.getName());
-                pw.print("  ");
-                pw.print(f.length());
-                pw.println(" bytes");
-            }
-        }
-
         @Override
         public void dumpDbInfo(final ParcelFileDescriptor pfd, final String[] args) {
             if (mSystemThread) {
@@ -5054,7 +5004,7 @@
     private void performConfigurationChangedForActivity(ActivityClientRecord r,
             Configuration newBaseConfig) {
         performConfigurationChangedForActivity(r, newBaseConfig,
-                r.activity.getDisplay().getDisplayId(), false /* movedToDifferentDisplay */);
+                r.activity.getDisplayId(), false /* movedToDifferentDisplay */);
     }
 
     /**
@@ -5406,7 +5356,7 @@
             return;
         }
         final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
-                && displayId != r.activity.getDisplay().getDisplayId();
+                && displayId != r.activity.getDisplayId();
 
         // Perform updates.
         r.overrideConfig = overrideConfig;
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 9b2bfc5..4b87a64 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -193,6 +193,13 @@
      */
     public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
 
+    /**
+     * Sent by Activity#startActivity to notify the entering activity that enter animation for
+     * back is allowed. If this message is not received, the default exit animation will run when
+     * backing out of an activity (instead of the 'reverse' shared element transition).
+     */
+    public static final int MSG_ALLOW_RETURN_TRANSITION = 108;
+
     private Window mWindow;
     final protected ArrayList<String> mAllSharedElementNames;
     final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -346,8 +353,6 @@
         return new ArrayList<View>(mSharedElements);
     }
 
-    public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
-
     protected Transition setTargets(Transition transition, boolean add) {
         if (transition == null || (add &&
                 (mTransitioningViews == null || mTransitioningViews.isEmpty()))) {
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index b8f5a8e..3201feb 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -35,7 +35,7 @@
  */
 class ActivityTransitionState {
 
-    private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";
+    private static final String PENDING_EXIT_SHARED_ELEMENTS = "android:pendingExitSharedElements";
 
     private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom";
 
@@ -43,9 +43,9 @@
 
     /**
      * The shared elements that the calling Activity has said that they transferred to this
-     * Activity.
+     * Activity and will be transferred back during exit animation.
      */
-    private ArrayList<String> mEnteringNames;
+    private ArrayList<String> mPendingExitNames;
 
     /**
      * The names of shared elements that were shared to the called Activity.
@@ -112,8 +112,7 @@
 
     public int addExitTransitionCoordinator(ExitTransitionCoordinator exitTransitionCoordinator) {
         if (mExitTransitionCoordinators == null) {
-            mExitTransitionCoordinators =
-                    new SparseArray<WeakReference<ExitTransitionCoordinator>>();
+            mExitTransitionCoordinators = new SparseArray<>();
         }
         WeakReference<ExitTransitionCoordinator> ref = new WeakReference(exitTransitionCoordinator);
         // clean up old references:
@@ -132,7 +131,7 @@
     public void readState(Bundle bundle) {
         if (bundle != null) {
             if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
-                mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
+                mPendingExitNames = bundle.getStringArrayList(PENDING_EXIT_SHARED_ELEMENTS);
             }
             if (mEnterTransitionCoordinator == null) {
                 mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
@@ -141,9 +140,21 @@
         }
     }
 
+    /**
+     * Returns the element names to be used for exit animation. It caches the list internally so
+     * that it is preserved through activty destroy and restore.
+     */
+    private ArrayList<String> getPendingExitNames() {
+        if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
+            mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
+        }
+        return mPendingExitNames;
+    }
+
     public void saveState(Bundle bundle) {
-        if (mEnteringNames != null) {
-            bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
+        ArrayList<String> pendingExitNames = getPendingExitNames();
+        if (pendingExitNames != null) {
+            bundle.putStringArrayList(PENDING_EXIT_SHARED_ELEMENTS, pendingExitNames);
         }
         if (mExitingFrom != null) {
             bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
@@ -226,7 +237,7 @@
             }
         } else {
             mEnterTransitionCoordinator.namedViewsReady(null, null);
-            mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
+            mPendingExitNames = null;
         }
 
         mExitingFrom = null;
@@ -268,7 +279,7 @@
     }
 
     public void clear() {
-        mEnteringNames = null;
+        mPendingExitNames = null;
         mExitingFrom = null;
         mExitingTo = null;
         mExitingToView = null;
@@ -296,7 +307,8 @@
     }
 
     public boolean startExitBackTransition(final Activity activity) {
-        if (mEnteringNames == null || mCalledExitCoordinator != null) {
+        ArrayList<String> pendingExitNames = getPendingExitNames();
+        if (pendingExitNames == null || mCalledExitCoordinator != null) {
             return false;
         } else {
             if (!mHasExited) {
@@ -315,7 +327,7 @@
                 }
 
                 mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
-                        activity.getWindow(), activity.mEnterTransitionListener, mEnteringNames,
+                        activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames,
                         null, null, true);
                 if (enterViewsTransition != null && decor != null) {
                     enterViewsTransition.resume(decor);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a05d01b..3b05566 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -431,9 +431,11 @@
     public static final int OP_BLUETOOTH_SCAN = 77;
     /** @hide Use the BiometricPrompt/BiometricManager APIs. */
     public static final int OP_USE_BIOMETRIC = 78;
+    /** @hide Physical activity recognition. */
+    public static final int OP_ACTIVITY_RECOGNITION = 79;
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 79;
+    public static final int _NUM_OP = 80;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -681,6 +683,9 @@
     /** @hide Use the BiometricPrompt/BiometricManager APIs. */
     public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
 
+    /** @hide Recognize physical activity. */
+    public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
+
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -722,6 +727,8 @@
             OP_CAMERA,
             // Body sensors
             OP_BODY_SENSORS,
+            // Activity recognition
+            OP_ACTIVITY_RECOGNITION,
 
             // APPOP PERMISSIONS
             OP_ACCESS_NOTIFICATIONS,
@@ -819,6 +826,7 @@
             OP_START_FOREGROUND,                // START_FOREGROUND
             OP_COARSE_LOCATION,                 // BLUETOOTH_SCAN
             OP_USE_BIOMETRIC,                   // BIOMETRIC
+            OP_ACTIVITY_RECOGNITION,            // ACTIVITY_RECOGNITION
     };
 
     /**
@@ -904,6 +912,7 @@
             OPSTR_START_FOREGROUND,
             OPSTR_BLUETOOTH_SCAN,
             OPSTR_USE_BIOMETRIC,
+            OPSTR_ACTIVITY_RECOGNITION,
     };
 
     /**
@@ -990,6 +999,7 @@
             "START_FOREGROUND",
             "BLUETOOTH_SCAN",
             "USE_BIOMETRIC",
+            "ACTIVITY_RECOGNITION",
     };
 
     /**
@@ -1077,6 +1087,7 @@
             Manifest.permission.FOREGROUND_SERVICE,
             null, // no permission for OP_BLUETOOTH_SCAN
             Manifest.permission.USE_BIOMETRIC,
+            Manifest.permission.ACTIVITY_RECOGNITION,
     };
 
     /**
@@ -1164,6 +1175,7 @@
             null, // START_FOREGROUND
             null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
             null, // USE_BIOMETRIC
+            null, // ACTIVITY_RECOGNITION
     };
 
     /**
@@ -1250,6 +1262,7 @@
             false, // START_FOREGROUND
             true, // BLUETOOTH_SCAN
             false, // USE_BIOMETRIC
+            false, // ACTIVITY_RECOGNITION
     };
 
     /**
@@ -1270,8 +1283,8 @@
             AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION
             AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS
             AppOpsManager.MODE_ALLOWED, // CALL_PHONE
-            AppOpsManager.MODE_DEFAULT, // READ_SMS
-            AppOpsManager.MODE_DEFAULT, // WRITE_SMS
+            AppOpsManager.MODE_ALLOWED, // READ_SMS
+            AppOpsManager.MODE_IGNORED, // WRITE_SMS
             AppOpsManager.MODE_DEFAULT, // RECEIVE_SMS
             AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST
             AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS
@@ -1335,6 +1348,7 @@
             AppOpsManager.MODE_ALLOWED, // START_FOREGROUND
             AppOpsManager.MODE_ALLOWED, // BLUETOOTH_SCAN
             AppOpsManager.MODE_ALLOWED, // USE_BIOMETRIC
+            AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
     };
 
     /**
@@ -1424,6 +1438,7 @@
             false, // START_FOREGROUND
             false, // BLUETOOTH_SCAN
             false, // USE_BIOMETRIC
+            false, // ACTIVITY_RECOGNITION
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 264029b..fcd9a05 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -55,6 +55,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.ArtManager;
@@ -85,6 +86,7 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.StructStat;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.IconDrawableFactory;
 import android.util.LauncherIcons;
@@ -2255,9 +2257,19 @@
     public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             String dialogMessage) {
+        final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage)
+                ? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build()
+                : null;
+        return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo);
+    }
+
+    @Override
+    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+            PersistableBundle appExtras, PersistableBundle launcherExtras,
+            SuspendDialogInfo dialogInfo) {
         try {
             return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
-                    launcherExtras, dialogMessage, mContext.getOpPackageName(),
+                    launcherExtras, dialogInfo, mContext.getOpPackageName(),
                     getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 77f6395..dc707e8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2088,8 +2088,7 @@
             ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
 
-            final int displayId = mDisplay != null
-                    ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+            final int displayId = getDisplayId();
 
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
@@ -2124,8 +2123,7 @@
             ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
                     flags, null);
 
-            final int displayId = mDisplay != null
-                    ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+            final int displayId = getDisplayId();
 
             c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
@@ -2152,8 +2150,7 @@
         final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
                 mActivityToken, mUser, mFlags, classLoader);
 
-        final int displayId = mDisplay != null
-                ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+        final int displayId = getDisplayId();
 
         context.setResources(ResourcesManager.getInstance().getResources(
                 mActivityToken,
@@ -2177,7 +2174,7 @@
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
-        final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+        final int displayId = getDisplayId();
         context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
@@ -2250,6 +2247,11 @@
     }
 
     @Override
+    public int getDisplayId() {
+        return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+    }
+
+    @Override
     public void updateDisplay(int displayId) {
         mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources);
     }
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index ab847fd..bce243c 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -65,6 +65,7 @@
     private OneShotPreDrawListener mViewsReadyListener;
     private final boolean mIsCrossTask;
     private Drawable mReplacedBackground;
+    private ArrayList<String> mPendingExitNames;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning, boolean isCrossTask) {
@@ -249,6 +250,11 @@
             case MSG_CANCEL:
                 cancel();
                 break;
+            case MSG_ALLOW_RETURN_TRANSITION:
+                if (!mIsCanceled) {
+                    mPendingExitNames = mAllSharedElementNames;
+                }
+                break;
         }
     }
 
@@ -256,6 +262,10 @@
         return mIsReturning && mResultReceiver != null;
     }
 
+    public ArrayList<String> getPendingExitSharedElementNames() {
+        return mPendingExitNames;
+    }
+
     /**
      * This is called onResume. If an Activity is resuming and the transitions
      * haven't started yet, force the views to appear. This is likely to be
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index df31da9..48a711e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -433,6 +433,11 @@
             if (!mSharedElementNotified) {
                 mSharedElementNotified = true;
                 delayCancel();
+
+                if (!mActivity.isTopOfTask()) {
+                    mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null);
+                }
+
                 if (mListener == null) {
                     mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
                     notifyExitComplete();
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index ceb828b..0e428ae 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -1013,6 +1013,11 @@
                     replaceTargets(sharedElementTransition, sharedElementsIn, null);
                 }
             }
+
+            @Override
+            public void onTransitionEnd(Transition transition) {
+                transition.removeListener(this);
+            }
         });
     }
 
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 848def6..9f93e17 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -603,9 +603,10 @@
     }
 
     /**
-     * @hide
+     * Returns whether the user has chosen the importance of this channel, either to affirm the
+     * initial selection from the app, or changed it to be higher or lower.
      */
-    public boolean isImportanceLocked() {
+    public boolean hasUserSetImportance() {
         return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0;
     }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0044005..77cebc8 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -377,11 +377,15 @@
                 return new DisplayManager(ctx.getOuterContext());
             }});
 
+        // InputMethodManager has its own cache strategy based on display id to support apps that
+        // still assume InputMethodManager is a per-process singleton and it's safe to directly
+        // access internal fields via reflection.  Hence directly use ServiceFetcher instead of
+        // StaticServiceFetcher/CachedServiceFetcher.
         registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class,
-                new StaticServiceFetcher<InputMethodManager>() {
+                new ServiceFetcher<InputMethodManager>() {
             @Override
-            public InputMethodManager createService() {
-                return InputMethodManager.getInstanceInternal();
+            public InputMethodManager getService(ContextImpl ctx) {
+                return InputMethodManager.forContext(ctx.getOuterContext());
             }});
 
         registerService(Context.TEXT_SERVICES_MANAGER_SERVICE, TextServicesManager.class,
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 9873a81..e33d1fe 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.app.slice.Slice;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -77,6 +78,7 @@
     final int mContextDescriptionResource;
     final boolean mShowMetadataInPreview;
     final boolean mSupportsAmbientMode;
+    final String mSettingsSliceUri;
 
     /**
      * Constructor.
@@ -118,7 +120,6 @@
                     com.android.internal.R.styleable.Wallpaper);
             mSettingsActivityName = sa.getString(
                     com.android.internal.R.styleable.Wallpaper_settingsActivity);
-
             mThumbnailResource = sa.getResourceId(
                     com.android.internal.R.styleable.Wallpaper_thumbnail,
                     -1);
@@ -140,6 +141,8 @@
             mSupportsAmbientMode = sa.getBoolean(
                     com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
                     false);
+            mSettingsSliceUri = sa.getString(
+                    com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
 
             sa.recycle();
         } catch (NameNotFoundException e) {
@@ -159,6 +162,7 @@
         mContextDescriptionResource = source.readInt();
         mShowMetadataInPreview = source.readInt() != 0;
         mSupportsAmbientMode = source.readInt() != 0;
+        mSettingsSliceUri = source.readString();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
     }
     
@@ -332,13 +336,28 @@
      * explicit {@link android.content.ComponentName}
      * composed of {@link #getPackageName} and the class name returned here.
      * 
-     * <p>A null will be returned if there is no settings activity associated
+     * <p>{@code null} will be returned if there is no settings activity associated
      * with the wallpaper.
      */
     public String getSettingsActivity() {
         return mSettingsActivityName;
     }
     
+    /**
+     * Returns an URI that provides a settings {@link Slice} for this wallpaper.
+     *
+     * <p>{@code null} will be returned if there is no settings Slice URI associated
+     * with the wallpaper.
+     *
+     * @return The URI.
+     */
+    public Uri getSettingsSliceUri() {
+        if (mSettingsSliceUri == null) {
+            return null;
+        }
+        return Uri.parse(mSettingsSliceUri);
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "Service:");
         mService.dump(pw, prefix + "  ");
@@ -367,6 +386,7 @@
         dest.writeInt(mContextDescriptionResource);
         dest.writeInt(mShowMetadataInPreview ? 1 : 0);
         dest.writeInt(mSupportsAmbientMode ? 1 : 0);
+        dest.writeString(mSettingsSliceUri);
         mService.writeToParcel(dest, flags);
     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 74fb4df..92daf08 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -51,6 +51,7 @@
 import android.graphics.Bitmap;
 import android.net.ProxyInfo;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
@@ -1898,6 +1899,36 @@
     public static final String ACTION_PROFILE_OWNER_CHANGED =
             "android.app.action.PROFILE_OWNER_CHANGED";
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"PRIVATE_DNS_MODE_"}, value = {
+            PRIVATE_DNS_MODE_UNKNOWN,
+            PRIVATE_DNS_MODE_OFF,
+            PRIVATE_DNS_MODE_OPPORTUNISTIC,
+            PRIVATE_DNS_MODE_PROVIDER_HOSTNAME
+    })
+    public @interface PrivateDnsMode {}
+
+    /**
+     * Specifies that the Private DNS setting is in an unknown state.
+     */
+    public static final int PRIVATE_DNS_MODE_UNKNOWN = 0;
+
+    /**
+     * Specifies that Private DNS was turned off completely.
+     */
+    public static final int PRIVATE_DNS_MODE_OFF = 1;
+
+    /**
+     * Specifies that the device owner requested opportunistic DNS over TLS
+     */
+    public static final int PRIVATE_DNS_MODE_OPPORTUNISTIC = 2;
+
+    /**
+     * Specifies that the device owner configured a specific host to use for Private DNS.
+     */
+    public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3;
+
     /**
      * Return true if the given administrator component is currently active (enabled) in the system.
      *
@@ -5756,7 +5787,8 @@
         }
         if (mService != null) {
             try {
-                return mService.checkDeviceIdentifierAccess(packageName, userId);
+                return mService.checkDeviceIdentifierAccess(packageName, userId,
+                        Binder.getCallingPid(), Binder.getCallingUid());
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -9754,4 +9786,80 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+
+    /**
+     * Sets the global Private DNS mode and host to be used.
+     * May only be called by the device owner.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param mode Which mode to set - either {@code PRIVATE_DNS_MODE_OPPORTUNISTIC} or
+     *             {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}.
+     *             Since the opportunistic mode defaults to ordinary DNS lookups, the
+     *             option to turn it completely off is not available, so this method
+     *             may not be called with {@code PRIVATE_DNS_MODE_OFF}.
+     * @param privateDnsHost The hostname of a server that implements DNS over TLS (RFC7858), if
+     *                       {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} was specified as the mode,
+     *                       null otherwise.
+     * @throws IllegalArgumentException in the following cases: if a {@code privateDnsHost} was
+     * provided but the mode was not {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME}, if the mode
+     * specified was {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} but {@code privateDnsHost} does
+     * not look like a valid hostname, or if the mode specified is not one of the two valid modes.
+     *
+     * @throws SecurityException if the caller is not the device owner.
+     */
+    public void setGlobalPrivateDns(@NonNull ComponentName admin,
+            @PrivateDnsMode int mode, @Nullable String privateDnsHost) {
+        throwIfParentInstance("setGlobalPrivateDns");
+        if (mService == null) {
+            return;
+        }
+
+        try {
+            mService.setGlobalPrivateDns(admin, mode, privateDnsHost);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the system-wide Private DNS mode.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @return one of {@code PRIVATE_DNS_MODE_OFF}, {@code PRIVATE_DNS_MODE_OPPORTUNISTIC},
+     * {@code PRIVATE_DNS_MODE_PROVIDER_HOSTNAME} or {@code PRIVATE_DNS_MODE_UNKNOWN}.
+     * @throws SecurityException if the caller is not the device owner.
+     */
+    public int getGlobalPrivateDnsMode(@NonNull ComponentName admin) {
+        throwIfParentInstance("setGlobalPrivateDns");
+        if (mService == null) {
+            return PRIVATE_DNS_MODE_UNKNOWN;
+        }
+
+        try {
+            return mService.getGlobalPrivateDnsMode(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the system-wide Private DNS host.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @return The hostname used for Private DNS queries.
+     * @throws SecurityException if the caller is not the device owner.
+     */
+    public String getGlobalPrivateDnsHost(@NonNull ComponentName admin) {
+        throwIfParentInstance("setGlobalPrivateDns");
+        if (mService == null) {
+            return null;
+        }
+
+        try {
+            return mService.getGlobalPrivateDnsHost(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5e45450..ce1f4ef 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -153,7 +153,7 @@
     void clearProfileOwner(in ComponentName who);
     boolean hasUserSetupCompleted();
 
-    boolean checkDeviceIdentifierAccess(in String packageName, int userHandle);
+    boolean checkDeviceIdentifierAccess(in String packageName, int userHandle, int pid, int uid);
 
     void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
     CharSequence getDeviceOwnerLockScreenInfo();
@@ -413,4 +413,8 @@
     boolean isOverrideApnEnabled(in ComponentName admin);
 
     boolean isMeteredDataDisabledPackageForUser(in ComponentName admin, String packageName, int userId);
+
+    void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
+    int getGlobalPrivateDnsMode(in ComponentName admin);
+    String getGlobalPrivateDnsHost(in ComponentName admin);
 }
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index fda2f89..cb996f3 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -183,7 +184,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index e7c8944..c447868 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -23,6 +23,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -138,7 +139,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth AVRCP Controller Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 9b7c173..d616b8f 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -57,6 +57,7 @@
     private int mAuthRetryState;
     private int mConnState;
     private final Object mStateLock = new Object();
+    private final Object mDeviceBusyLock = new Object();
     @UnsupportedAppUsage
     private Boolean mDeviceBusy = false;
     @UnsupportedAppUsage
@@ -282,7 +283,7 @@
                         }
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
                 }
@@ -357,7 +358,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -413,7 +414,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -496,7 +497,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -547,7 +548,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -596,7 +597,7 @@
                         return;
                     }
 
-                    synchronized (mDeviceBusy) {
+                    synchronized (mDeviceBusyLock) {
                         mDeviceBusy = false;
                     }
 
@@ -1098,7 +1099,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1132,7 +1133,7 @@
         if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid);
         if (mService == null || mClientIf == 0) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1178,7 +1179,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1221,7 +1222,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1262,7 +1263,7 @@
         BluetoothDevice device = service.getDevice();
         if (device == null) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
@@ -1330,7 +1331,7 @@
         if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
-        synchronized (mDeviceBusy) {
+        synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) return false;
             mDeviceBusy = true;
         }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index ec18d42..549c1fa 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -428,7 +429,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Headset Client Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index b967fb2..22d41d9 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -491,7 +492,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 606f00a..47c4ee6 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -29,6 +29,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -205,7 +206,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                android.os.Process.myUserHandle())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
             return;
         }
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 3bc8544..e44f36e 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -24,6 +24,7 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -454,7 +455,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 0ca39f1..58a2522 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -25,6 +25,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -279,7 +280,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 98c23c6..fc5f830 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -110,7 +111,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 559a59b..1c82e19 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -128,7 +129,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 58be732..8923d73 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -26,6 +26,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -150,7 +151,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index ae264e1..a601df0 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -24,6 +24,7 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -164,7 +165,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 1446adc..cbc96c0 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -23,6 +23,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -116,7 +117,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent);
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 1b73206..ebf6bed 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -148,7 +149,7 @@
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
-                mContext.getUser())) {
+                UserHandle.CURRENT_OR_SELF)) {
             Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
             return false;
         }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 2a03787..145c9273 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -822,6 +822,48 @@
     }
 
     /**
+     * Opaque token representing the identity of an incoming IPC.
+     */
+    public final class CallingIdentity {
+        /** {@hide} */
+        public final long binderToken;
+        /** {@hide} */
+        public final String callingPackage;
+
+        /** {@hide} */
+        public CallingIdentity(long binderToken, String callingPackage) {
+            this.binderToken = binderToken;
+            this.callingPackage = callingPackage;
+        }
+    }
+
+    /**
+     * Reset the identity of the incoming IPC on the current thread.
+     * <p>
+     * Internally this calls {@link Binder#clearCallingIdentity()} and also
+     * clears any value stored in {@link #getCallingPackage()}.
+     *
+     * @return Returns an opaque token that can be used to restore the original
+     *         calling identity by passing it to
+     *         {@link #restoreCallingIdentity}.
+     */
+    public final @NonNull CallingIdentity clearCallingIdentity() {
+        return new CallingIdentity(Binder.clearCallingIdentity(), setCallingPackage(null));
+    }
+
+    /**
+     * Restore the identity of the incoming IPC on the current thread back to a
+     * previously identity that was returned by {@link #clearCallingIdentity}.
+     * <p>
+     * Internally this calls {@link Binder#restoreCallingIdentity(long)} and
+     * also restores any value stored in {@link #getCallingPackage()}.
+     */
+    public final void restoreCallingIdentity(@NonNull CallingIdentity identity) {
+        Binder.restoreCallingIdentity(identity.binderToken);
+        mCallingPackage.set(identity.callingPackage);
+    }
+
+    /**
      * Change the authorities of the ContentProvider.
      * This is normally set for you from its manifest information when the provider is first
      * created.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c19909d..a2a6b9b 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -35,6 +35,10 @@
 import android.database.CrossProcessCursorWrapper;
 import android.database.Cursor;
 import android.database.IContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.ImageInfo;
+import android.graphics.ImageDecoder.Source;
 import android.graphics.Point;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -52,6 +56,7 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Size;
 
 import com.android.internal.util.MimeIconUtils;
 import com.android.internal.util.Preconditions;
@@ -68,6 +73,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -3188,4 +3194,71 @@
         }
         return query;
     }
+
+    /**
+     * Convenience method that efficiently loads a visual thumbnail for the
+     * given {@link Uri}. Internally calls
+     * {@link ContentProvider#openTypedAssetFile} on the remote provider, but
+     * also defensively resizes any returned content to match the requested
+     * target size.
+     *
+     * @param uri The item that should be visualized as a thumbnail.
+     * @param size The target area on the screen where this thumbnail will be
+     *            shown. This is passed to the provider as {@link #EXTRA_SIZE}
+     *            to help it avoid downloading or generating heavy resources.
+     * @param signal A signal to cancel the operation in progress.
+     * @return Valid {@link Bitmap} which is a visual thumbnail.
+     * @throws IOException If any trouble was encountered while generating or
+     *             loading the thumbnail, or if
+     *             {@link CancellationSignal#cancel()} was invoked.
+     */
+    public @NonNull Bitmap loadThumbnail(@NonNull Uri uri, @NonNull Size size,
+            @Nullable CancellationSignal signal) throws IOException {
+        Objects.requireNonNull(uri);
+        Objects.requireNonNull(size);
+
+        try (ContentProviderClient client = acquireContentProviderClient(uri)) {
+            return loadThumbnail(client, uri, size, signal, ImageDecoder.ALLOCATOR_DEFAULT);
+        }
+    }
+
+    /** {@hide} */
+    public static Bitmap loadThumbnail(@NonNull ContentProviderClient client, @NonNull Uri uri,
+            @NonNull Size size, @Nullable CancellationSignal signal, int allocator)
+            throws IOException {
+        Objects.requireNonNull(client);
+        Objects.requireNonNull(uri);
+        Objects.requireNonNull(size);
+
+        // Convert to Point, since that's what the API is defined as
+        final Bundle opts = new Bundle();
+        opts.putParcelable(EXTRA_SIZE, Point.convert(size));
+
+        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(() -> {
+            return client.openTypedAssetFileDescriptor(uri, "image/*", opts, signal);
+        }), (ImageDecoder decoder, ImageInfo info, Source source) -> {
+            decoder.setAllocator(allocator);
+
+            // One last-ditch check to see if we've been canceled.
+            if (signal != null) signal.throwIfCanceled();
+
+            // We requested a rough thumbnail size, but the remote size may have
+            // returned something giant, so defensively scale down as needed.
+            final int widthSample = info.getSize().getWidth() / size.getWidth();
+            final int heightSample = info.getSize().getHeight() / size.getHeight();
+            final int sample = Math.min(widthSample, heightSample);
+            if (sample > 1) {
+                decoder.setTargetSampleSize(sample);
+            }
+        });
+    }
+
+    /** {@hide} */
+    public static void onDbCorruption(String tag, String message, Throwable stacktrace) {
+        try {
+            getContentService().onDbCorruption(tag, message, Log.getStackTraceString(stacktrace));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index db4adcf..3197352 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4982,6 +4982,14 @@
     public abstract Display getDisplay();
 
     /**
+     * Gets the display ID.
+     *
+     * @return display ID associated with this {@link Context}.
+     * @hide
+     */
+    public abstract int getDisplayId();
+
+    /**
      * @hide
      */
     public abstract void updateDisplay(int displayId);
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index c5dce017..bfad2b4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -921,6 +921,14 @@
      * @hide
      */
     @Override
+    public int getDisplayId() {
+        return mBase.getDisplayId();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
     public void updateDisplay(int displayId) {
         mBase.updateDisplay(displayId);
     }
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index a55dd31..1d02375 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -185,4 +185,6 @@
     Bundle getCache(in String packageName, in Uri key, int userId);
 
     void resetTodayStats();
+
+    void onDbCorruption(String tag, String message, String stacktrace);
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8913748..ea1a2fe 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1908,6 +1908,22 @@
     @SystemApi
     public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
 
+    /**
+     * Activity action: Launch UI to review app uses of permissions.
+     * <p>
+     * Input: Nothing
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REVIEW_PERMISSION_USAGE =
+            "android.intent.action.REVIEW_PERMISSION_USAGE";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
@@ -4453,7 +4469,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";
 
     /**
@@ -5009,8 +5024,7 @@
             "android.intent.extra.user_handle";
 
     /**
-     * The UserHandle carried with broadcasts intents related to addition and removal of managed
-     * profiles - {@link #ACTION_MANAGED_PROFILE_ADDED} and {@link #ACTION_MANAGED_PROFILE_REMOVED}.
+     * The UserHandle carried with intents.
      */
     public static final String EXTRA_USER =
             "android.intent.extra.USER";
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6a20c93..4a4de51 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -43,6 +43,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
@@ -273,7 +274,7 @@
 
     String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
             in PersistableBundle appExtras, in PersistableBundle launcherExtras,
-            String dialogMessage, String callingPackage, int userId);
+            in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
 
     boolean isPackageSuspendedForUser(String packageName, int userId);
 
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 00aa5c2..cdb7814 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -16,10 +16,12 @@
 
 package android.content.pm;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_FIRST_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_SINGLE_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
+import static android.text.TextUtils.makeSafeForPresentation;
 
 import android.annotation.FloatRange;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.res.XmlResourceParser;
@@ -27,17 +29,13 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.UserHandle;
-import android.text.Html;
-import android.text.TextPaint;
 import android.text.TextUtils;
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.Preconditions;
 
-import java.lang.annotation.Retention;
 import java.text.Collator;
-import java.util.BitSet;
 import java.util.Comparator;
 
 /**
@@ -50,9 +48,6 @@
  * in the implementation of Parcelable in subclasses.
  */
 public class PackageItemInfo {
-    private static final int LINE_FEED_CODE_POINT = 10;
-    private static final int NBSP_CODE_POINT = 160;
-
     /** The maximum length of a safe label, in characters */
     private static final int MAX_SAFE_LABEL_LENGTH = 50000;
 
@@ -60,45 +55,43 @@
     public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f;
 
     /**
-     * Flags for {@link #loadSafeLabel(PackageManager, float, int)}
-     *
-     * @hide
-     */
-    @Retention(SOURCE)
-    @IntDef(flag = true, prefix = "SAFE_LABEL_FLAG_",
-            value = {SAFE_LABEL_FLAG_TRIM, SAFE_LABEL_FLAG_SINGLE_LINE,
-                    SAFE_LABEL_FLAG_FIRST_LINE})
-    public @interface SafeLabelFlags {}
-
-    /**
      * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
      * of the label.
      *
      * @see #loadSafeLabel(PackageManager, float, int)
+     *
+     * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_TRIM} instead
      * @hide
      */
+    @Deprecated
     @SystemApi
-    public static final int SAFE_LABEL_FLAG_TRIM = 0x1;
+    public static final int SAFE_LABEL_FLAG_TRIM = SAFE_STRING_FLAG_TRIM;
 
     /**
      * Force entire string into single line of text (no newlines). Cannot be set at the same time as
      * {@link #SAFE_LABEL_FLAG_FIRST_LINE}.
      *
      * @see #loadSafeLabel(PackageManager, float, int)
+     *
+     * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_SINGLE_LINE} instead
      * @hide
      */
+    @Deprecated
     @SystemApi
-    public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 0x2;
+    public static final int SAFE_LABEL_FLAG_SINGLE_LINE = SAFE_STRING_FLAG_SINGLE_LINE;
 
     /**
      * Return only first line of text (truncate at first newline). Cannot be set at the same time as
      * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}.
      *
      * @see #loadSafeLabel(PackageManager, float, int)
+     *
+     * @deprecated Use {@link TextUtils#SAFE_STRING_FLAG_FIRST_LINE} instead
      * @hide
      */
+    @Deprecated
     @SystemApi
-    public static final int SAFE_LABEL_FLAG_FIRST_LINE = 0x4;
+    public static final int SAFE_LABEL_FLAG_FIRST_LINE = SAFE_STRING_FLAG_FIRST_LINE;
 
     private static volatile boolean sForceSafeLabels = false;
 
@@ -199,8 +192,8 @@
      */
     public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
         if (sForceSafeLabels) {
-            return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_LABEL_FLAG_TRIM
-                    | SAFE_LABEL_FLAG_FIRST_LINE);
+            return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
+                    | SAFE_STRING_FLAG_FIRST_LINE);
         } else {
             return loadUnsafeLabel(pm);
         }
@@ -223,16 +216,6 @@
         return packageName;
     }
 
-    private static boolean isNewline(int codePoint) {
-        int type = Character.getType(codePoint);
-        return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
-                || codePoint == LINE_FEED_CODE_POINT;
-    }
-
-    private static boolean isWhiteSpace(int codePoint) {
-        return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
-    }
-
     /**
      * @hide
      * @deprecated use loadSafeLabel(PackageManager, float, int) instead
@@ -240,209 +223,24 @@
     @SystemApi
     @Deprecated
     public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
-        return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_LABEL_FLAG_TRIM
-                | SAFE_LABEL_FLAG_FIRST_LINE);
+        return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
+                | SAFE_STRING_FLAG_FIRST_LINE);
     }
 
     /**
-     * A special string manipulation class. Just records removals and executes the when onString()
-     * is called.
-     */
-    private static class StringWithRemovedChars {
-        /** The original string */
-        private final String mOriginal;
-
-        /**
-         * One bit per char in string. If bit is set, character needs to be removed. If whole
-         * bit field is not initialized nothing needs to be removed.
-         */
-        private BitSet mRemovedChars;
-
-        StringWithRemovedChars(@NonNull String original) {
-            mOriginal = original;
-        }
-
-        /**
-         * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including
-         * firstNonRemoved) as removed.
-         */
-        void removeRange(int firstRemoved, int firstNonRemoved) {
-            if (mRemovedChars == null) {
-                mRemovedChars = new BitSet(mOriginal.length());
-            }
-
-            mRemovedChars.set(firstRemoved, firstNonRemoved);
-        }
-
-        /**
-         * Remove all characters before {@code firstNonRemoved}.
-         */
-        void removeAllCharBefore(int firstNonRemoved) {
-            if (mRemovedChars == null) {
-                mRemovedChars = new BitSet(mOriginal.length());
-            }
-
-            mRemovedChars.set(0, firstNonRemoved);
-        }
-
-        /**
-         * Remove all characters after and including {@code firstRemoved}.
-         */
-        void removeAllCharAfter(int firstRemoved) {
-            if (mRemovedChars == null) {
-                mRemovedChars = new BitSet(mOriginal.length());
-            }
-
-            mRemovedChars.set(firstRemoved, mOriginal.length());
-        }
-
-        @Override
-        public String toString() {
-            // Common case, no chars removed
-            if (mRemovedChars == null) {
-                return mOriginal;
-            }
-
-            StringBuilder sb = new StringBuilder(mOriginal.length());
-            for (int i = 0; i < mOriginal.length(); i++) {
-                if (!mRemovedChars.get(i)) {
-                    sb.append(mOriginal.charAt(i));
-                }
-            }
-
-            return sb.toString();
-        }
-
-        /**
-         * Return length or the original string
-         */
-        int length() {
-            return mOriginal.length();
-        }
-
-        /**
-         * Return if a certain {@code offset} of the original string is removed
-         */
-        boolean isRemoved(int offset) {
-            return mRemovedChars != null && mRemovedChars.get(offset);
-        }
-
-        /**
-         * Return codePoint of original string at a certain {@code offset}
-         */
-        int codePointAt(int offset) {
-            return mOriginal.codePointAt(offset);
-        }
-    }
-
-    /**
-     * Load, clean up and truncate label before use.
+     * Calls {@link TextUtils#makeSafeForPresentation} for the label of this item.
      *
-     * <p>This method is meant to remove common mistakes and nefarious formatting from strings that
-     * are used in sensitive parts of the UI.
+     * <p>For parameters see {@link TextUtils#makeSafeForPresentation}.
      *
-     * <p>This method first treats the string like HTML and then ...
-     * <ul>
-     * <li>Removes new lines or truncates at first new line
-     * <li>Trims the white-space off the end
-     * <li>Truncates the string to a given length
-     * </ul>
-     * ... if specified.
-     *
-     * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42.
-     *                     This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br />
-     *                     Usually ellipsizing should be left to the view showing the string. If a
-     *                     string is used as an input to another string, it might be useful to
-     *                     control the length of the input string though. {@code 0} disables this
-     *                     feature.
-     * @return The safe label
      * @hide
-     */
+    */
     @SystemApi
     public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm,
-            @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags) {
-        boolean onlyKeepFirstLine = ((flags & SAFE_LABEL_FLAG_FIRST_LINE) != 0);
-        boolean forceSingleLine = ((flags & SAFE_LABEL_FLAG_SINGLE_LINE) != 0);
-        boolean trim = ((flags & SAFE_LABEL_FLAG_TRIM) != 0);
-
+            @FloatRange(from = 0) float ellipsizeDip, @TextUtils.SafeStringFlags int flags) {
         Preconditions.checkNotNull(pm);
-        Preconditions.checkArgument(ellipsizeDip >= 0);
-        Preconditions.checkFlagsArgument(flags, SAFE_LABEL_FLAG_TRIM | SAFE_LABEL_FLAG_SINGLE_LINE
-                | SAFE_LABEL_FLAG_FIRST_LINE);
-        Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine),
-                "Cannot set SAFE_LABEL_FLAG_SINGLE_LINE and SAFE_LABEL_FLAG_FIRST_LINE at the same "
-                + "time");
 
-        // loadLabel() always returns non-null
-        String label = loadUnsafeLabel(pm).toString();
-
-        // Treat string as HTML. This
-        // - converts HTML symbols: e.g. &szlig; -> ß
-        // - applies some HTML tags: e.g. <br> -> \n
-        // - removes invalid characters such as \b
-        // - removes html styling, such as <b>
-        // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc
-        // - replaces some html tags by "object replacement" markers: <img> -> \ufffc
-        // - Removes leading white space
-        // - Removes all trailing white space beside a single space
-        // - Collapses double white space
-        StringWithRemovedChars labelStr = new StringWithRemovedChars(
-                Html.fromHtml(label).toString());
-
-        int firstNonWhiteSpace = -1;
-        int firstTrailingWhiteSpace = -1;
-
-        // Remove new lines (if requested) and control characters.
-        int labelLength = labelStr.length();
-        for (int offset = 0; offset < labelLength; ) {
-            int codePoint = labelStr.codePointAt(offset);
-            int type = Character.getType(codePoint);
-            int codePointLen = Character.charCount(codePoint);
-            boolean isNewline = isNewline(codePoint);
-
-            if (offset > MAX_SAFE_LABEL_LENGTH || onlyKeepFirstLine && isNewline) {
-                labelStr.removeAllCharAfter(offset);
-                break;
-            } else if (forceSingleLine && isNewline) {
-                labelStr.removeRange(offset, offset + codePointLen);
-            } else if (type == Character.CONTROL && !isNewline) {
-                labelStr.removeRange(offset, offset + codePointLen);
-            } else if (trim && !isWhiteSpace(codePoint)) {
-                // This is only executed if the code point is not removed
-                if (firstNonWhiteSpace == -1) {
-                    firstNonWhiteSpace = offset;
-                }
-                firstTrailingWhiteSpace = offset + codePointLen;
-            }
-
-            offset += codePointLen;
-        }
-
-        if (trim) {
-            // Remove leading and trailing white space
-            if (firstNonWhiteSpace == -1) {
-                // No non whitespace found, remove all
-                labelStr.removeAllCharAfter(0);
-            } else {
-                if (firstNonWhiteSpace > 0) {
-                    labelStr.removeAllCharBefore(firstNonWhiteSpace);
-                }
-                if (firstTrailingWhiteSpace < labelLength) {
-                    labelStr.removeAllCharAfter(firstTrailingWhiteSpace);
-                }
-            }
-        }
-
-        if (ellipsizeDip == 0) {
-            return labelStr.toString();
-        } else {
-            // Truncate
-            final TextPaint paint = new TextPaint();
-            paint.setTextSize(42);
-
-            return TextUtils.ellipsize(labelStr.toString(), paint, ellipsizeDip,
-                    TextUtils.TruncateAt.END);
-        }
+        return makeSafeForPresentation(loadUnsafeLabel(pm).toString(), MAX_SAFE_LABEL_LENGTH,
+                ellipsizeDip, flags);
     }
 
     /**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 733fbe5..dfb8128 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5664,7 +5664,7 @@
      * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
      *
      * @param packageNames The names of the packages to set the suspended status.
-     * @param suspended If set to {@code true} than the packages will be suspended, if set to
+     * @param suspended If set to {@code true}, the packages will be suspended, if set to
      * {@code false}, the packages will be unsuspended.
      * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
      *                  which will be shared with the apps being suspended. Ignored if
@@ -5676,15 +5676,76 @@
      *                      suspended app.
      *
      * @return an array of package names for which the suspended status could not be set as
-     * requested in this method.
+     * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+     *
+     * @deprecated use {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, android.content.pm.SuspendDialogInfo)} instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+    @Nullable
+    public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
+            @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+            @Nullable String dialogMessage) {
+        throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+    }
+
+    /**
+     * Puts the given packages in a suspended state, where attempts at starting activities are
+     * denied.
+     *
+     * <p>The suspended application's notifications and all of its windows will be hidden, any
+     * of its started activities will be stopped and it won't be able to ring the device.
+     * It doesn't remove the data or the actual package file.
+     *
+     * <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
+     * is suspended will be shown instead.
+     * The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
+     * to this api. This dialog will have a button that starts the
+     * {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
+     * activity which handles this action.
+     *
+     * <p>The packages being suspended must already be installed. If a package is uninstalled, it
+     * will no longer be suspended.
+     *
+     * <p>Optionally, the suspending app can provide extra information in the form of
+     * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+     * launcher to support customization that they might need to handle the suspended state.
+     *
+     * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
+     *
+     * @param packageNames The names of the packages to set the suspended status.
+     * @param suspended If set to {@code true}, the packages will be suspended, if set to
+     * {@code false}, the packages will be unsuspended.
+     * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+     *                  which will be shared with the apps being suspended. Ignored if
+     *                  {@code suspended} is false.
+     * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+     *                       provide which will be shared with the launcher. Ignored if
+     *                       {@code suspended} is false.
+     * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
+     *                   should be shown to the user when they try to launch a suspended app.
+     *                   Ignored if {@code suspended} is false.
+     *
+     * @return an array of package names for which the suspended status could not be set as
+     * requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
+     *
+     * @see #isPackageSuspended
+     * @see SuspendDialogInfo
+     * @see SuspendDialogInfo.Builder
+     * @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+    @Nullable
+    public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
             @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
-            String dialogMessage) {
+            @Nullable SuspendDialogInfo dialogInfo) {
         throw new UnsupportedOperationException("setPackagesSuspended not implemented");
     }
 
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 7b4c6fc..4f58321 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -267,14 +267,15 @@
     public abstract String getSuspendingPackage(String suspendedPackage, int userId);
 
     /**
-     * Get the dialog message to be shown to the user when they try to launch a suspended
-     * application.
+     * Get the information describing the dialog to be shown to the user when they try to launch a
+     * suspended application.
      *
      * @param suspendedPackage The package that has been suspended.
      * @param userId The user for which to check.
-     * @return The dialog message to be shown to the user.
+     * @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
      */
-    public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
+    @Nullable
+    public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
 
     /**
      * Do a straight uid lookup for the given package/application in the given user.
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 248d523..e21c33a 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -33,6 +33,7 @@
 import android.os.PersistableBundle;
 import android.util.ArraySet;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
 import java.util.Arrays;
@@ -50,7 +51,7 @@
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
     public String suspendingPackage;
-    public String dialogMessage; // Message to show when a suspended package launch attempt is made
+    public SuspendDialogInfo dialogInfo;
     public PersistableBundle suspendedAppExtras;
     public PersistableBundle suspendedLauncherExtras;
     public boolean instantApp;
@@ -79,6 +80,7 @@
         installReason = PackageManager.INSTALL_REASON_UNKNOWN;
     }
 
+    @VisibleForTesting
     public PackageUserState(PackageUserState o) {
         ceDataInode = o.ceDataInode;
         installed = o.installed;
@@ -87,7 +89,7 @@
         hidden = o.hidden;
         suspended = o.suspended;
         suspendingPackage = o.suspendingPackage;
-        dialogMessage = o.dialogMessage;
+        dialogInfo = o.dialogInfo;
         suspendedAppExtras = o.suspendedAppExtras;
         suspendedLauncherExtras = o.suspendedLauncherExtras;
         instantApp = o.instantApp;
@@ -217,7 +219,7 @@
                     || !suspendingPackage.equals(oldState.suspendingPackage)) {
                 return false;
             }
-            if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
+            if (!Objects.equals(dialogInfo, oldState.dialogInfo)) {
                 return false;
             }
             if (!BaseBundle.kindofEquals(suspendedAppExtras,
diff --git a/core/java/android/content/pm/SuspendDialogInfo.aidl b/core/java/android/content/pm/SuspendDialogInfo.aidl
new file mode 100644
index 0000000..5e711cf
--- /dev/null
+++ b/core/java/android/content/pm/SuspendDialogInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+parcelable SuspendDialogInfo;
diff --git a/core/java/android/content/pm/SuspendDialogInfo.java b/core/java/android/content/pm/SuspendDialogInfo.java
new file mode 100644
index 0000000..c798c99
--- /dev/null
+++ b/core/java/android/content/pm/SuspendDialogInfo.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.res.ResourceId.ID_NULL;
+
+import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.content.res.ResourceId;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A container to describe the dialog to be shown when the user tries to launch a suspended
+ * application.
+ * The suspending app can customize the dialog's following attributes:
+ * <ul>
+ * <li>The dialog icon, by providing a resource id.
+ * <li>The title text, by providing a resource id.
+ * <li>The text of the dialog's body, by providing a resource id or a string.
+ * <li>The text on the neutral button which starts the
+ * {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS SHOW_SUSPENDED_APP_DETAILS}
+ * activity, by providing a resource id.
+ * </ul>
+ * System defaults are used whenever any of these are not provided, or any of the provided resource
+ * ids cannot be resolved at the time of displaying the dialog.
+ *
+ * @hide
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * SuspendDialogInfo)
+ * @see Builder
+ */
+@SystemApi
+public final class SuspendDialogInfo implements Parcelable {
+    private static final String TAG = SuspendDialogInfo.class.getSimpleName();
+    private static final String XML_ATTR_ICON_RES_ID = "iconResId";
+    private static final String XML_ATTR_TITLE_RES_ID = "titleResId";
+    private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
+    private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
+    private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
+
+    private final int mIconResId;
+    private final int mTitleResId;
+    private final int mDialogMessageResId;
+    private final String mDialogMessage;
+    private final int mNeutralButtonTextResId;
+
+    /**
+     * @return the resource id of the icon to be used with the dialog
+     * @hide
+     */
+    @DrawableRes
+    public int getIconResId() {
+        return mIconResId;
+    }
+
+    /**
+     * @return the resource id of the title to be used with the dialog
+     * @hide
+     */
+    @StringRes
+    public int getTitleResId() {
+        return mTitleResId;
+    }
+
+    /**
+     * @return the resource id of the text to be shown in the dialog's body
+     * @hide
+     */
+    @StringRes
+    public int getDialogMessageResId() {
+        return mDialogMessageResId;
+    }
+
+    /**
+     * @return the text to be shown in the dialog's body. Returns {@code null} if
+     * {@link #getDialogMessageResId()} returns a valid resource id.
+     * @hide
+     */
+    @Nullable
+    public String getDialogMessage() {
+        return mDialogMessage;
+    }
+
+    /**
+     * @return the text to be shown
+     * @hide
+     */
+    @StringRes
+    public int getNeutralButtonTextResId() {
+        return mNeutralButtonTextResId;
+    }
+
+    /**
+     * @hide
+     */
+    public void saveToXml(XmlSerializer out) throws IOException {
+        if (mIconResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId);
+        }
+        if (mTitleResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId);
+        }
+        if (mDialogMessageResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
+        } else {
+            XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage);
+        }
+        if (mNeutralButtonTextResId != ID_NULL) {
+            XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static SuspendDialogInfo restoreFromXml(XmlPullParser in) {
+        final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder();
+        try {
+            final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL);
+            final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
+            final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
+                    ID_NULL);
+            final int dialogMessageResId = XmlUtils.readIntAttribute(
+                    in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
+            final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
+
+            if (iconId != ID_NULL) {
+                dialogInfoBuilder.setIcon(iconId);
+            }
+            if (titleId != ID_NULL) {
+                dialogInfoBuilder.setTitle(titleId);
+            }
+            if (buttonTextId != ID_NULL) {
+                dialogInfoBuilder.setNeutralButtonText(buttonTextId);
+            }
+            if (dialogMessageResId != ID_NULL) {
+                dialogInfoBuilder.setMessage(dialogMessageResId);
+            } else if (dialogMessage != null) {
+                dialogInfoBuilder.setMessage(dialogMessage);
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception while parsing from xml. Some fields may default", e);
+        }
+        return dialogInfoBuilder.build();
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = mIconResId;
+        hashCode = 31 * hashCode + mTitleResId;
+        hashCode = 31 * hashCode + mNeutralButtonTextResId;
+        hashCode = 31 * hashCode + mDialogMessageResId;
+        hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SuspendDialogInfo)) {
+            return false;
+        }
+        final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
+        return mIconResId == otherDialogInfo.mIconResId
+                && mTitleResId == otherDialogInfo.mTitleResId
+                && mDialogMessageResId == otherDialogInfo.mDialogMessageResId
+                && mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
+                && Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder("SuspendDialogInfo: {");
+        if (mIconResId != ID_NULL) {
+            builder.append("mIconId = 0x");
+            builder.append(Integer.toHexString(mIconResId));
+            builder.append(" ");
+        }
+        if (mTitleResId != ID_NULL) {
+            builder.append("mTitleResId = 0x");
+            builder.append(Integer.toHexString(mTitleResId));
+            builder.append(" ");
+        }
+        if (mNeutralButtonTextResId != ID_NULL) {
+            builder.append("mNeutralButtonTextResId = 0x");
+            builder.append(Integer.toHexString(mNeutralButtonTextResId));
+            builder.append(" ");
+        }
+        if (mDialogMessageResId != ID_NULL) {
+            builder.append("mDialogMessageResId = 0x");
+            builder.append(Integer.toHexString(mDialogMessageResId));
+            builder.append(" ");
+        } else if (mDialogMessage != null) {
+            builder.append("mDialogMessage = \"");
+            builder.append(mDialogMessage);
+            builder.append("\" ");
+        }
+        builder.append("}");
+        return builder.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(mIconResId);
+        dest.writeInt(mTitleResId);
+        dest.writeInt(mDialogMessageResId);
+        dest.writeString(mDialogMessage);
+        dest.writeInt(mNeutralButtonTextResId);
+    }
+
+    private SuspendDialogInfo(Parcel source) {
+        mIconResId = source.readInt();
+        mTitleResId = source.readInt();
+        mDialogMessageResId = source.readInt();
+        mDialogMessage = source.readString();
+        mNeutralButtonTextResId = source.readInt();
+    }
+
+    SuspendDialogInfo(Builder b) {
+        mIconResId = b.mIconResId;
+        mTitleResId = b.mTitleResId;
+        mDialogMessageResId = b.mDialogMessageResId;
+        mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
+        mNeutralButtonTextResId = b.mNeutralButtonTextResId;
+    }
+
+    public static final Creator<SuspendDialogInfo> CREATOR = new Creator<SuspendDialogInfo>() {
+        @Override
+        public SuspendDialogInfo createFromParcel(Parcel source) {
+            return new SuspendDialogInfo(source);
+        }
+
+        @Override
+        public SuspendDialogInfo[] newArray(int size) {
+            return new SuspendDialogInfo[size];
+        }
+    };
+
+    /**
+     * Builder to build a {@link SuspendDialogInfo} object.
+     */
+    public static final class Builder {
+        private int mDialogMessageResId = ID_NULL;
+        private String mDialogMessage;
+        private int mTitleResId = ID_NULL;
+        private int mIconResId = ID_NULL;
+        private int mNeutralButtonTextResId = ID_NULL;
+
+        /**
+         * Set the resource id of the icon to be used. If not provided, no icon will be shown.
+         *
+         * @param resId The resource id of the icon.
+         * @return this builder object.
+         */
+        @NonNull
+        public Builder setIcon(@DrawableRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mIconResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the resource id of the title text to be displayed. If this is not provided, the
+         * system will use a default title.
+         *
+         * @param resId The resource id of the title.
+         * @return this builder object.
+         */
+        @NonNull
+        public Builder setTitle(@StringRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mTitleResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the text to show in the body of the dialog. Ignored if a resource id is set via
+         * {@link #setMessage(int)}.
+         * <p>
+         * The system will use {@link String#format(Locale, String, Object...) String.format} to
+         * insert the suspended app name into the message, so an example format string could be
+         * {@code "The app %1$s is currently suspended"}. This is optional - if the string passed in
+         * {@code message} does not accept an argument, it will be used as is.
+         *
+         * @param message The dialog message.
+         * @return this builder object.
+         * @see #setMessage(int)
+         */
+        @NonNull
+        public Builder setMessage(@NonNull String message) {
+            Preconditions.checkStringNotEmpty(message, "Message cannot be null or empty");
+            mDialogMessage = message;
+            return this;
+        }
+
+        /**
+         * Set the resource id of the dialog message to be shown. If no dialog message is provided
+         * via either this method or {@link #setMessage(String)}, the system will use a
+         * default message.
+         * <p>
+         * The system will use {@link android.content.res.Resources#getString(int, Object...)
+         * getString} to insert the suspended app name into the message, so an example format string
+         * could be {@code "The app %1$s is currently suspended"}. This is optional - if the string
+         * referred to by {@code resId} does not accept an argument, it will be used as is.
+         *
+         * @param resId The resource id of the dialog message.
+         * @return this builder object.
+         * @see #setMessage(String)
+         */
+        @NonNull
+        public Builder setMessage(@StringRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mDialogMessageResId = resId;
+            return this;
+        }
+
+        /**
+         * Set the resource id of text to be shown on the neutral button. Tapping this button starts
+         * the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity. If this is
+         * not provided, the system will use a default text.
+         *
+         * @param resId The resource id of the button text
+         * @return this builder object.
+         */
+        @NonNull
+        public Builder setNeutralButtonText(@StringRes int resId) {
+            Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
+            mNeutralButtonTextResId = resId;
+            return this;
+        }
+
+        /**
+         * Build the final object based on given inputs.
+         *
+         * @return The {@link SuspendDialogInfo} object built using this builder.
+         */
+        @NonNull
+        public SuspendDialogInfo build() {
+            return new SuspendDialogInfo(this);
+        }
+    }
+}
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
index 7fa2b40..cf019e1 100755
--- a/core/java/android/database/DefaultDatabaseErrorHandler.java
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.java
@@ -15,14 +15,14 @@
  */
 package android.database;
 
-import java.io.File;
-import java.util.List;
-
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
 import android.util.Log;
 import android.util.Pair;
 
+import java.io.File;
+import java.util.List;
+
 /**
  * Default class used to define the action to take when database corruption is reported
  * by sqlite.
@@ -52,6 +52,7 @@
      */
     public void onCorruption(SQLiteDatabase dbObj) {
         Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
+        SQLiteDatabase.wipeDetected(dbObj.getPath(), "corruption");
 
         // is the corruption detected even before database could be 'opened'?
         if (!dbObj.isOpen()) {
@@ -99,7 +100,7 @@
         }
         Log.e(TAG, "deleting the database file: " + fileName);
         try {
-            SQLiteDatabase.deleteDatabase(new File(fileName));
+            SQLiteDatabase.deleteDatabase(new File(fileName), /*removeCheckFile=*/ false);
         } catch (Exception e) {
             /* print warning and ignore exception */
             Log.w(TAG, "delete failed: " + e.getMessage());
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 5c4f16a..20505ca 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -34,6 +34,7 @@
 import dalvik.system.CloseGuard;
 
 import java.io.File;
+import java.io.IOException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
@@ -414,6 +415,10 @@
         final String newLocale = mConfiguration.locale.toString();
         nativeRegisterLocalizedCollators(mConnectionPtr, newLocale);
 
+        if (!mConfiguration.isInMemoryDb()) {
+            checkDatabaseWiped();
+        }
+
         // If the database is read-only, we cannot modify the android metadata table
         // or existing indexes.
         if (mIsReadOnlyConnection) {
@@ -449,6 +454,36 @@
         }
     }
 
+    private void checkDatabaseWiped() {
+        if (!SQLiteGlobal.checkDbWipe()) {
+            return;
+        }
+        try {
+            final File checkFile = new File(mConfiguration.path
+                    + SQLiteGlobal.WIPE_CHECK_FILE_SUFFIX);
+
+            final boolean hasMetadataTable = executeForLong(
+                    "SELECT count(*) FROM sqlite_master"
+                            + " WHERE type='table' AND name='android_metadata'", null, null) > 0;
+            final boolean hasCheckFile = checkFile.exists();
+
+            if (!mIsReadOnlyConnection && !hasCheckFile) {
+                // Create the check file, unless it's a readonly connection,
+                // in which case we can't create the metadata table anyway.
+                checkFile.createNewFile();
+            }
+
+            if (!hasMetadataTable && hasCheckFile) {
+                // Bad. The DB is gone unexpectedly.
+                SQLiteDatabase.wipeDetected(mConfiguration.path, "unknown");
+            }
+
+        } catch (RuntimeException | IOException ex) {
+            SQLiteDatabase.wtfAsSystemServer(TAG,
+                    "Unexpected exception while checking for wipe", ex);
+        }
+    }
+
     // Called by SQLiteConnectionPool only.
     void reconfigure(SQLiteDatabaseConfiguration configuration) {
         mOnlyAllowReadOnlyOperations = false;
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 3ee348b..dbc1766 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -24,6 +24,7 @@
 import android.os.OperationCanceledException;
 import android.os.SystemClock;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.PrefixPrinter;
 import android.util.Printer;
@@ -34,6 +35,7 @@
 import dalvik.system.CloseGuard;
 
 import java.io.Closeable;
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -1105,9 +1107,12 @@
      * @param printer The printer to receive the dump, not null.
      * @param verbose True to dump more verbose information.
      */
-    public void dump(Printer printer, boolean verbose) {
+    public void dump(Printer printer, boolean verbose, ArraySet<String> directories) {
         Printer indentedPrinter = PrefixPrinter.create(printer, "    ");
         synchronized (mLock) {
+            if (directories != null) {
+                directories.add(new File(mConfiguration.path).getParent());
+            }
             printer.println("Connection pool for " + mConfiguration.path + ":");
             printer.println("  Open: " + mIsOpen);
             printer.println("  Max connections: " + mMaxConnectionPoolSize);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index eb5c720..f9c2c3e 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -22,6 +22,8 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.DatabaseErrorHandler;
@@ -34,6 +36,7 @@
 import android.os.OperationCanceledException;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Pair;
@@ -45,9 +48,14 @@
 
 import java.io.File;
 import java.io.FileFilter;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -808,6 +816,12 @@
      * @return True if the database was successfully deleted.
      */
     public static boolean deleteDatabase(@NonNull File file) {
+        return deleteDatabase(file, /*removeCheckFile=*/ true);
+    }
+
+
+    /** @hide */
+    public static boolean deleteDatabase(@NonNull File file, boolean removeCheckFile) {
         if (file == null) {
             throw new IllegalArgumentException("file must not be null");
         }
@@ -818,6 +832,9 @@
         deleted |= new File(file.getPath() + "-shm").delete();
         deleted |= new File(file.getPath() + "-wal").delete();
 
+        // This file is not a standard SQLite file, so don't update the deleted flag.
+        new File(file.getPath() + SQLiteGlobal.WIPE_CHECK_FILE_SUFFIX).delete();
+
         File dir = file.getParentFile();
         if (dir != null) {
             final String prefix = file.getName() + "-mj";
@@ -2170,21 +2187,61 @@
      * Dump detailed information about all open databases in the current process.
      * Used by bug report.
      */
-    static void dumpAll(Printer printer, boolean verbose) {
+    static void dumpAll(Printer printer, boolean verbose, boolean isSystem) {
+        // Use this ArraySet to collect file paths.
+        final ArraySet<String> directories = new ArraySet<>();
+
         for (SQLiteDatabase db : getActiveDatabases()) {
-            db.dump(printer, verbose);
+            db.dump(printer, verbose, isSystem, directories);
+        }
+
+        // Dump DB files in the directories.
+        if (directories.size() > 0) {
+            final String[] dirs = directories.toArray(new String[directories.size()]);
+            Arrays.sort(dirs);
+            for (String dir : dirs) {
+                dumpDatabaseDirectory(printer, new File(dir), isSystem);
+            }
         }
     }
 
-    private void dump(Printer printer, boolean verbose) {
+    private void dump(Printer printer, boolean verbose, boolean isSystem, ArraySet directories) {
         synchronized (mLock) {
             if (mConnectionPoolLocked != null) {
                 printer.println("");
-                mConnectionPoolLocked.dump(printer, verbose);
+                mConnectionPoolLocked.dump(printer, verbose, directories);
             }
         }
     }
 
+    private static void dumpDatabaseDirectory(Printer pw, File dir, boolean isSystem) {
+        pw.println("");
+        pw.println("Database files in " + dir.getAbsolutePath() + ":");
+        final File[] files = dir.listFiles();
+        if (files == null || files.length == 0) {
+            pw.println("  [none]");
+            return;
+        }
+        Arrays.sort(files, (a, b) -> a.getName().compareTo(b.getName()));
+
+        for (File f : files) {
+            if (isSystem) {
+                // If called within the system server, the directory contains other files too, so
+                // filter by file extensions.
+                // (If it's an app, just print all files because they may not use *.db
+                // extension.)
+                final String name = f.getName();
+                if (!(name.endsWith(".db") || name.endsWith(".db-wal")
+                        || name.endsWith(".db-journal")
+                        || name.endsWith(SQLiteGlobal.WIPE_CHECK_FILE_SUFFIX))) {
+                    continue;
+                }
+            }
+            pw.println(String.format("  %-40s %7db %s", f.getName(), f.length(),
+                    SQLiteDatabase.getFileTimestamps(f.getAbsolutePath())));
+        }
+    }
+
     /**
      * Returns list of full pathnames of all attached databases including the main database
      * by executing 'pragma database_list' on the database.
@@ -2611,7 +2668,7 @@
                 return this;
             }
 
-            /**
+            /**w
              * Sets <a href="https://sqlite.org/pragma.html#pragma_synchronous">synchronous mode</a>
              * .
              * @return
@@ -2646,5 +2703,34 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface DatabaseOpenFlags {}
 
+    /** @hide */
+    public static void wipeDetected(String filename, String reason) {
+        wtfAsSystemServer(TAG, "DB wipe detected:"
+                + " package=" + ActivityThread.currentPackageName()
+                + " reason=" + reason
+                + " file=" + filename
+                + " " + getFileTimestamps(filename)
+                + " checkfile " + getFileTimestamps(filename + SQLiteGlobal.WIPE_CHECK_FILE_SUFFIX),
+                new Throwable("STACKTRACE"));
+    }
+
+    /** @hide */
+    public static String getFileTimestamps(String path) {
+        try {
+            BasicFileAttributes attr = Files.readAttributes(
+                    FileSystems.getDefault().getPath(path), BasicFileAttributes.class);
+            return "ctime=" + attr.creationTime()
+                    + " mtime=" + attr.lastModifiedTime()
+                    + " atime=" + attr.lastAccessTime();
+        } catch (IOException e) {
+            return "[unable to obtain timestamp]";
+        }
+    }
+
+    /** @hide */
+    static void wtfAsSystemServer(String tag, String message, Throwable stacktrace) {
+        Log.e(tag, message, stacktrace);
+        ContentResolver.onDbCorruption(tag, message, stacktrace);
+    }
 }
 
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 1c66204..f220205 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -189,6 +189,11 @@
      * @param args Command-line arguments supplied to dumpsys dbinfo
      */
     public static void dump(Printer printer, String[] args) {
+        dump(printer, args, false);
+    }
+
+    /** @hide */
+    public static void dump(Printer printer, String[] args, boolean isSystem) {
         boolean verbose = false;
         for (String arg : args) {
             if (arg.equals("-v")) {
@@ -196,6 +201,6 @@
             }
         }
 
-        SQLiteDatabase.dumpAll(printer, verbose);
+        SQLiteDatabase.dumpAll(printer, verbose, isSystem);
     }
 }
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index 67e5f65..ff286fd 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -42,6 +42,9 @@
     /** @hide */
     public static final String SYNC_MODE_FULL = "FULL";
 
+    /** @hide */
+    static final String WIPE_CHECK_FILE_SUFFIX = "-wipecheck";
+
     private static final Object sLock = new Object();
 
     private static int sDefaultPageSize;
@@ -181,4 +184,8 @@
                         com.android.internal.R.integer.db_wal_truncate_size));
     }
 
+    /** @hide */
+    public static boolean checkDbWipe() {
+        return true;
+    }
 }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 09113e5..01ef58e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -397,7 +397,7 @@
         if (display == null) {
             // TODO: We cannot currently provide any override configurations for metrics on displays
             // other than the display the context is associated with.
-            final Context context = mContext.getDisplay().getDisplayId() == displayId
+            final Context context = mContext.getDisplayId() == displayId
                     ? mContext : mContext.getApplicationContext();
 
             display = mGlobal.getCompatibleDisplay(displayId, context.getResources());
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 6609b76..56da719 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -105,12 +105,15 @@
      *
      * This method should be used if the caller wants to receive notifications even after the
      * process exits. The client must have an open connection with the Context Hub Service (i.e. it
-     * cannot have been closed through the {@link #close()} method). If registered successfully,
-     * intents will be delivered regarding events for the specified nanoapp from the attached
-     * Context Hub. Any unicast messages for this client will also be delivered. The intent will
-     * have an extra {@link #EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which will
-     * contain the type of the event. See {@link ContextHubManager.Event} for description of each
-     * event type.
+     * cannot have been closed through the {@link #close()} method). Only one PendingIntent can be
+     * registered at a time for a single ContextHubClient. If registered successfully, intents will
+     * be delivered regarding events for the specified nanoapp from the attached Context Hub. Any
+     * unicast messages for this client will also be delivered. The intent will have an extra
+     * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
+     * describes the Context Hub the intent event was for. The intent will also have an extra
+     * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
+     * will contain the type of the event. See {@link ContextHubManager.Event} for description of
+     * each event type, along with event-specific extra fields.
      *
      * When the intent is received, this client can be recreated through
      * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo,
@@ -127,30 +130,42 @@
      * {@link PendingIntent} through a {@link BroadcastReceiver}, and maps an {@link Intent} to a
      * {@link ContextHubClientCallback}.
      *
-     * @param intent    The PendingIntent to register for this client
-     * @param nanoAppId the unique ID of the nanoapp to receive events for
+     * @param pendingIntent the PendingIntent to register for this client
+     * @param nanoAppId     the unique ID of the nanoapp to receive events for
      * @return true on success, false otherwise
      *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public boolean registerIntent(@NonNull PendingIntent intent, long nanoAppId) {
-        // TODO: Implement this
-        return false;
+    public boolean registerIntent(@NonNull PendingIntent pendingIntent, long nanoAppId) {
+        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
+
+        try {
+            return mClientProxy.registerIntent(pendingIntent, nanoAppId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * Unregisters an intent previously registered via {@link #registerIntent(PendingIntent, long)}.
      * If this intent has not been registered for this client, this method returns false.
      *
+     * @param pendingIntent the PendingIntent to unregister
+     *
      * @return true on success, false otherwise
      *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public boolean unregisterIntent(@NonNull PendingIntent intent) {
-        // TODO: Implement this
-        return false;
+    public boolean unregisterIntent(@NonNull PendingIntent pendingIntent) {
+        Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
+
+        try {
+            return mClientProxy.unregisterIntent(pendingIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index f94d69b..b0b77f3 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -73,7 +73,7 @@
      *
      * @hide
      */
-    public static final String EXTRA_NANOAPP_ID = "android.location.hardware.extra.NANOAPP_ID";
+    public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
 
     /**
      * An extra of type int describing the nanoapp-specific abort code.
@@ -81,14 +81,14 @@
      * @hide
      */
     public static final String EXTRA_NANOAPP_ABORT_CODE =
-            "android.location.hardware.extra.NANOAPP_ABORT_CODE";
+            "android.hardware.location.extra.NANOAPP_ABORT_CODE";
 
     /**
      * An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
      *
      * @hide
      */
-    public static final String EXTRA_MESSAGE = "android.location.hardware.extra.MESSAGE";
+    public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
 
     /**
      * Constants describing the type of events from a Context Hub.
@@ -800,22 +800,22 @@
      * through {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} or
      * equivalent at an earlier time.
      *
-     * @param intent   the intent that is associated with a client
-     * @param hubInfo  the hub to attach this client to
-     * @param callback the notification callback to register
-     * @param executor the executor to invoke the callback
+     * @param pendingIntent the PendingIntent that has been registered with a client
+     * @param hubInfo       the hub to attach this client to
+     * @param callback      the notification callback to register
+     * @param executor      the executor to invoke the callback
      * @return the registered client object
      *
-     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or the intent
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
      *                                  was not associated with a client
      * @throws IllegalStateException    if there were too many registered clients at the service
-     * @throws NullPointerException     if intent, hubInfo, callback, or executor is null
+     * @throws NullPointerException     if pendingIntent, hubInfo, callback, or executor is null
      *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @NonNull public ContextHubClient createClient(
-            @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo,
+            @NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
             @NonNull ContextHubClientCallback callback,
             @NonNull @CallbackExecutor Executor executor) {
         // TODO: Implement this
@@ -823,26 +823,27 @@
     }
 
     /**
-     * Equivalent to {@link #createClient(Intent, ContextHubInfo, ContextHubClientCallback,
+     * Equivalent to {@link #createClient(PendingIntent, ContextHubInfo, ContextHubClientCallback,
      * Executor)} with the executor using the main thread's Looper.
      *
-     * @param intent   the intent that is associated with a client
-     * @param hubInfo  the hub to attach this client to
-     * @param callback the notification callback to register
+     * @param pendingIntent the PendingIntent that has been registered with a client
+     * @param hubInfo       the hub to attach this client to
+     * @param callback      the notification callback to register
      * @return the registered client object
      *
-     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or the intent
+     * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
      *                                  was not associated with a client
      * @throws IllegalStateException    if there were too many registered clients at the service
-     * @throws NullPointerException     if intent, hubInfo, or callback is null
+     * @throws NullPointerException     if pendingIntent, hubInfo, or callback is null
      *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @NonNull public ContextHubClient createClient(
-            @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo,
+            @NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
             @NonNull ContextHubClientCallback callback) {
-        return createClient(intent, hubInfo, callback, new HandlerExecutor(Handler.getMain()));
+        return createClient(
+                pendingIntent, hubInfo, callback, new HandlerExecutor(Handler.getMain()));
     }
 
     /**
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
index d81126a..7559cd5 100644
--- a/core/java/android/hardware/location/IContextHubClient.aidl
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.location;
 
+import android.app.PendingIntent;
 import android.hardware.location.NanoAppMessage;
 
 /**
@@ -28,4 +29,10 @@
 
     // Closes the connection with the Context Hub
     void close();
+
+    // Registers a PendingIntent with the client
+    boolean registerIntent(in PendingIntent intent, long nanoAppId);
+
+    // Unregisters a PendingIntent from the client
+    boolean unregisterIntent(in PendingIntent intent);
 }
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 838765b..e970747 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+
 import java.lang.ref.WeakReference;
 
 /**
@@ -131,6 +132,14 @@
     @UnsupportedAppUsage
     public native int stopRecognition(int soundModelHandle);
 
+    /**
+     * Get the current state of a {@link SoundTrigger.SoundModel}
+     * @param soundModelHandle The sound model handle indicating which model's state to return
+     * @return - {@link SoundTrigger#RecognitionEvent} in case of success
+     *         - null in case of an error or if not supported
+     */
+    public native SoundTrigger.RecognitionEvent getModelState(int soundModelHandle);
+
     private class NativeEventHandlerDelegate {
         private final Handler mHandler;
 
@@ -207,4 +216,3 @@
         }
     }
 }
-
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 0982d65..843db6d 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -41,8 +41,7 @@
 class IInputMethodSessionWrapper extends IInputMethodSession.Stub
         implements HandlerCaller.Callback {
     private static final String TAG = "InputMethodWrapper";
-    
-    private static final int DO_FINISH_INPUT = 60;
+
     private static final int DO_DISPLAY_COMPLETIONS = 65;
     private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
     private static final int DO_UPDATE_SELECTION = 90;
@@ -89,9 +88,6 @@
         }
 
         switch (msg.what) {
-            case DO_FINISH_INPUT:
-                mInputMethodSession.finishInput();
-                return;
             case DO_DISPLAY_COMPLETIONS:
                 mInputMethodSession.displayCompletions((CompletionInfo[])msg.obj);
                 return;
@@ -150,11 +146,6 @@
     }
 
     @Override
-    public void finishInput() {
-        mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
-    }
-
-    @Override
     public void displayCompletions(CompletionInfo[] completions) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(
                 DO_DISPLAY_COMPLETIONS, completions));
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1fbfa40..4714587 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2816,10 +2816,11 @@
          * @param network The {@link Network} of the satisfying network.
          * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
          * @param linkProperties The {@link LinkProperties} of the satisfying network.
+         * @param blocked Whether access to the {@link Network} is blocked due to system policy.
          * @hide
          */
         public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
-                LinkProperties linkProperties) {
+                LinkProperties linkProperties, boolean blocked) {
             // Internally only this method is called when a new network is available, and
             // it calls the callback in the same way and order that older versions used
             // to call so as not to change the behavior.
@@ -2830,6 +2831,7 @@
             }
             onCapabilitiesChanged(network, networkCapabilities);
             onLinkPropertiesChanged(network, linkProperties);
+            onBlockedStatusChanged(network, blocked);
         }
 
         /**
@@ -2837,7 +2839,8 @@
          * This callback may be called more than once if the {@link Network} that is
          * satisfying the request changes. This will always immediately be followed by a
          * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
-         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
+         * {@link #onBlockedStatusChanged(Network, boolean)}.
          *
          * @param network The {@link Network} of the satisfying network.
          */
@@ -2916,6 +2919,14 @@
          */
         public void onNetworkResumed(Network network) {}
 
+        /**
+         * Called when access to the specified network is blocked or unblocked.
+         *
+         * @param network The {@link Network} whose blocked status has changed.
+         * @param blocked The blocked status of this {@link Network}.
+         */
+        public void onBlockedStatusChanged(Network network, boolean blocked) {}
+
         private NetworkRequest networkRequest;
     }
 
@@ -2962,6 +2973,8 @@
     public static final int CALLBACK_SUSPENDED           = BASE + 9;
     /** @hide */
     public static final int CALLBACK_RESUMED             = BASE + 10;
+    /** @hide */
+    public static final int CALLBACK_BLK_CHANGED         = BASE + 11;
 
     /** @hide */
     public static String getCallbackName(int whichCallback) {
@@ -2976,6 +2989,7 @@
             case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
             case CALLBACK_SUSPENDED:    return "CALLBACK_SUSPENDED";
             case CALLBACK_RESUMED:      return "CALLBACK_RESUMED";
+            case CALLBACK_BLK_CHANGED:  return "CALLBACK_BLK_CHANGED";
             default:
                 return Integer.toString(whichCallback);
         }
@@ -3022,7 +3036,7 @@
                 case CALLBACK_AVAILABLE: {
                     NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
                     LinkProperties lp = getObject(message, LinkProperties.class);
-                    callback.onAvailable(network, cap, lp);
+                    callback.onAvailable(network, cap, lp, message.arg1 != 0);
                     break;
                 }
                 case CALLBACK_LOSING: {
@@ -3055,6 +3069,10 @@
                     callback.onNetworkResumed(network);
                     break;
                 }
+                case CALLBACK_BLK_CHANGED: {
+                    boolean blocked = message.arg1 != 0;
+                    callback.onBlockedStatusChanged(network, blocked);
+                }
             }
         }
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 12b6f9e..0bdfca7 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1590,4 +1590,14 @@
         Preconditions.checkArgument(isValidCapability(capability),
                 "NetworkCapability " + capability + "out of range");
     }
+
+    /**
+     * Check if this {@code NetworkCapability} instance is metered.
+     *
+     * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+     * @hide
+     */
+    public boolean isMetered() {
+        return !hasCapability(NET_CAPABILITY_NOT_METERED);
+    }
 }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index d912dd10..1a1d2d334 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -202,7 +202,9 @@
      * Return a network-type-specific integer describing the subtype
      * of the network.
      * @return the network subtype
+     * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
      */
+    @Deprecated
     public int getSubtype() {
         synchronized (this) {
             return mSubtype;
@@ -243,7 +245,9 @@
     /**
      * Return a human-readable name describing the subtype of the network.
      * @return the name of the network subtype
+     * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
      */
+    @Deprecated
     public String getSubtypeName() {
         synchronized (this) {
             return mSubtypeName;
@@ -278,7 +282,15 @@
      * connections and pass data.
      * <p>Always call this before attempting to perform data transactions.
      * @return {@code true} if network connectivity exists, {@code false} otherwise.
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes. See
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isConnected() {
         synchronized (this) {
             return mState == State.CONNECTED;
@@ -411,7 +423,15 @@
     /**
      * Reports the current fine-grained state of the network.
      * @return the fine-grained state
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes. See
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public DetailedState getDetailedState() {
         synchronized (this) {
             return mDetailedState;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 34e9476..c431e40e 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,8 +16,13 @@
 
 package android.net;
 
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import android.annotation.NonNull;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
+import android.system.Os;
 import android.util.Log;
 import android.util.Pair;
 
@@ -570,4 +575,30 @@
         }
         return routedIPCount;
     }
+
+    private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET, AF_INET6};
+
+    /**
+     * Returns true if the hostname is weakly validated.
+     * @param hostname Name of host to validate.
+     * @return True if it's a valid-ish hostname.
+     *
+     * @hide
+     */
+    public static boolean isWeaklyValidatedHostname(@NonNull String hostname) {
+        // TODO(b/34953048): Use a validation method that permits more accurate,
+        // but still inexpensive, checking of likely valid DNS hostnames.
+        final String weakHostnameRegex = "^[a-zA-Z0-9_.-]+$";
+        if (!hostname.matches(weakHostnameRegex)) {
+            return false;
+        }
+
+        for (int address_family : ADDRESS_FAMILIES) {
+            if (Os.inet_pton(address_family, hostname) != null) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 40465ce..d09f33b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1102,19 +1102,18 @@
         public String getHost() {
             @SuppressWarnings("StringEquality")
             boolean cached = (host != NOT_CACHED);
-            return cached ? host
-                    : (host = parseHost());
+            return cached ? host : (host = parseHost());
         }
 
         private String parseHost() {
-            String authority = getEncodedAuthority();
+            final String authority = getEncodedAuthority();
             if (authority == null) {
                 return null;
             }
 
             // Parse out user info and then port.
             int userInfoSeparator = authority.lastIndexOf('@');
-            int portSeparator = authority.indexOf(':', userInfoSeparator);
+            int portSeparator = findPortSeparator(authority);
 
             String encodedHost = portSeparator == NOT_FOUND
                     ? authority.substring(userInfoSeparator + 1)
@@ -1132,16 +1131,8 @@
         }
 
         private int parsePort() {
-            String authority = getEncodedAuthority();
-            if (authority == null) {
-                return -1;
-            }
-
-            // Make sure we look for the port separtor *after* the user info
-            // separator. We have URLs with a ':' in the user info.
-            int userInfoSeparator = authority.lastIndexOf('@');
-            int portSeparator = authority.indexOf(':', userInfoSeparator);
-
+            final String authority = getEncodedAuthority();
+            int portSeparator = findPortSeparator(authority);
             if (portSeparator == NOT_FOUND) {
                 return -1;
             }
@@ -1154,6 +1145,24 @@
                 return -1;
             }
         }
+
+        private int findPortSeparator(String authority) {
+            if (authority == null) {
+                return NOT_FOUND;
+            }
+
+            // Reverse search for the ':' character that breaks as soon as a char that is neither
+            // a colon nor an ascii digit is encountered. Thanks to the goodness of UTF-16 encoding,
+            // it's not possible that a surrogate matches one of these, so this loop can just
+            // look for characters rather than care about code points.
+            for (int i = authority.length() - 1; i >= 0; --i) {
+                final int character = authority.charAt(i);
+                if (':' == character) return i;
+                // Character.isDigit would include non-ascii digits
+                if (character < '0' || character > '9') return NOT_FOUND;
+            }
+            return NOT_FOUND;
+        }
     }
 
     /**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f947b5e..8b6194c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -28,6 +28,8 @@
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 
+import dalvik.annotation.optimization.CriticalNative;
+
 import libcore.io.IoUtils;
 import libcore.util.NativeAllocationRegistry;
 
@@ -373,6 +375,54 @@
     public static final native int getThreadStrictModePolicy();
 
     /**
+     * Sets the work source for this thread.
+     *
+     * <p>All the following binder calls on this thread will use the provided work source.
+     *
+     * <p>The concept of worksource is similar to {@link WorkSource}. However, for performance
+     * reasons, we only support one UID. This UID represents the original user responsible for the
+     * binder calls.
+     *
+     * <p>A typical use case would be
+     * <pre>
+     * Binder.setThreadWorkSource(uid);
+     * try {
+     *   // Call an API.
+     * } finally {
+     *   Binder.clearThreadWorkSource();
+     * }
+     * </pre>
+     *
+     * @param workSource The original UID responsible for the binder call.
+     * @return The previously set work source.
+     * @hide
+     **/
+    @CriticalNative
+    public static final native int setThreadWorkSource(int workSource);
+
+    /**
+     * Returns the work source set by the caller.
+     *
+     * Unlike {@link Binder#getCallingUid()}, this result of this method cannot be trusted. The
+     * caller can set the value to whatever he wants. Only use this value if you trust the calling
+     * uid.
+     *
+     * @return The original UID responsible for the binder transaction.
+     * @hide
+     */
+    @CriticalNative
+    public static final native int getThreadWorkSource();
+
+    /**
+     * Clears the work source on this thread.
+     *
+     * @return The previously set work source.
+     * @hide
+     **/
+    @CriticalNative
+    public static final native int clearThreadWorkSource();
+
+    /**
      * Flush any Binder commands pending in the current thread to the kernel
      * driver.  This can be
      * useful to call before performing an operation that may block for a long
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 412a700..292543c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -130,9 +130,9 @@
      * <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain
      * proof of the device's original identifiers.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. Profile owner access is
+     * deprecated and will be removed in a future release.
      *
      * @return The serial number if specified.
      */
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index a9cb0d9..f71fdd7 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1198,6 +1198,19 @@
 
     /** {@hide} */
     public static int translateModeStringToPosix(String mode) {
+        // Sanity check for invalid chars
+        for (int i = 0; i < mode.length(); i++) {
+            switch (mode.charAt(i)) {
+                case 'r':
+                case 'w':
+                case 't':
+                case 'a':
+                    break;
+                default:
+                    throw new IllegalArgumentException("Bad mode: " + mode);
+            }
+        }
+
         int res = 0;
         if (mode.startsWith("rw")) {
             res |= O_RDWR | O_CREAT;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 5f65620..0c56d48 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
 import android.opengl.EGL14;
 import android.os.Build;
 import android.os.SystemProperties;
@@ -27,7 +29,14 @@
 
 import dalvik.system.VMRuntime;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashSet;
+import java.util.Set;
 
 /** @hide */
 public class GraphicsEnvironment {
@@ -44,8 +53,10 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "GraphicsEnvironment";
     private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+    private static final String PROPERTY_GFX_DRIVER_WHITELIST = "ro.gfx.driver.whitelist.0";
     private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
     private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
+    private static final String ANGLE_RULES_FILE = "a4a_rules.json";
 
     private ClassLoader mClassLoader;
     private String mLayerPath;
@@ -55,7 +66,7 @@
      * Set up GraphicsEnvironment
      */
     public void setup(Context context, Bundle coreSettings) {
-        setupGpuLayers(context);
+        setupGpuLayers(context, coreSettings);
         setupAngle(context, coreSettings);
         chooseDriver(context);
     }
@@ -81,27 +92,54 @@
     }
 
     /**
+     * Return the debug layer app's on-disk and in-APK lib directories
+     */
+    private static String getDebugLayerAppPaths(Context context, String app) {
+        ApplicationInfo appInfo;
+        try {
+            appInfo = context.getPackageManager().getApplicationInfo(
+                    app, PackageManager.MATCH_ALL);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Debug layer app '" + app + "' not installed");
+
+            return null;
+        }
+
+        String abi = chooseAbi(appInfo);
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(appInfo.nativeLibraryDir)
+            .append(File.pathSeparator);
+        sb.append(appInfo.sourceDir)
+            .append("!/lib/")
+            .append(abi);
+        String paths = sb.toString();
+
+        if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths);
+
+        return paths;
+    }
+
+    /**
      * Set up layer search paths for all apps
      * If debuggable, check for additional debug settings
      */
-    private void setupGpuLayers(Context context) {
+    private void setupGpuLayers(Context context, Bundle coreSettings) {
 
         String layerPaths = "";
 
         // Only enable additional debug functionality if the following conditions are met:
-        // 1. App is debuggable
+        // 1. App is debuggable or device is rooted
         // 2. ENABLE_GPU_DEBUG_LAYERS is true
         // 3. Package name is equal to GPU_DEBUG_APP
 
-        if (isDebuggable(context)) {
+        if (isDebuggable(context) || (getCanLoadSystemLibraries() == 1)) {
 
-            int enable = Settings.Global.getInt(context.getContentResolver(),
-                                                Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+            int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
 
             if (enable != 0) {
 
-                String gpuDebugApp = Settings.Global.getString(context.getContentResolver(),
-                                                               Settings.Global.GPU_DEBUG_APP);
+                String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP);
 
                 String packageName = context.getPackageName();
 
@@ -115,8 +153,22 @@
                     // the layers specified by the app.
                     layerPaths = mDebugLayerPath + ":";
 
-                    String layers = Settings.Global.getString(context.getContentResolver(),
-                                                              Settings.Global.GPU_DEBUG_LAYERS);
+
+                    // If there is a debug layer app specified, add its path.
+                    String gpuDebugLayerApp =
+                            coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP);
+
+                    if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) {
+                        Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp);
+                        String paths = getDebugLayerAppPaths(context, gpuDebugLayerApp);
+                        if (paths != null) {
+                            // Append the path so files placed in the app's base directory will
+                            // override the external path
+                            layerPaths += paths + ":";
+                        }
+                    }
+
+                    String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS);
 
                     Log.i(TAG, "Debug layer list: " + layers);
                     if (layers != null && !layers.isEmpty()) {
@@ -201,8 +253,40 @@
 
         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
 
+        // Pass the rules file to loader for ANGLE decisions
+        AssetManager angleAssets = null;
+        try {
+            angleAssets =
+                context.getPackageManager().getResourcesForApplication(angleInfo).getAssets();
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "Failed to get AssetManager for '" + ANGLE_PACKAGE_NAME + "'");
+            return;
+        }
+
+        AssetFileDescriptor assetsFd = null;
+        try {
+            assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE + " from "
+                       + "'" + ANGLE_PACKAGE_NAME + "'");
+            return;
+        }
+
+        FileDescriptor rulesFd = null;
+        long rulesOffset = 0;
+        long rulesLength = 0;
+        if (assetsFd != null) {
+            rulesFd = assetsFd.getFileDescriptor();
+            rulesOffset = assetsFd.getStartOffset();
+            rulesLength = assetsFd.getLength();
+        } else {
+            Log.w(TAG, "Failed to get file descriptor for " + ANGLE_RULES_FILE);
+            return;
+        }
+
         // Further opt-in logic is handled in native, so pass relevant info down
-        setAngleInfo(paths, packageName, appPref, devOptIn);
+        setAngleInfo(paths, packageName, appPref, devOptIn,
+                     rulesFd, rulesOffset, rulesLength);
     }
 
     /**
@@ -221,6 +305,15 @@
             if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
             return;
         }
+        Set<String> whitelist = loadWhitelist(context, driverPackageName);
+
+        // Empty whitelist implies no updatable graphics driver. Typically, the pre-installed
+        // updatable graphics driver is supposed to be a place holder and contains no graphics
+        // driver and whitelist.
+        if (whitelist == null || whitelist.isEmpty()) {
+            return;
+        }
+
         ApplicationInfo driverInfo;
         try {
             driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
@@ -229,6 +322,22 @@
             Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
             return;
         }
+        if (!whitelist.contains(context.getPackageName())) {
+            if (DEBUG) {
+                Log.w(TAG, context.getPackageName() + " is not on the whitelist.");
+            }
+            return;
+        }
+
+        // O drivers are restricted to the sphal linker namespace, so don't try to use
+        // packages unless they declare they're compatible with that restriction.
+        if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+            if (DEBUG) {
+                Log.w(TAG, "updated driver package is not known to be compatible with O");
+            }
+            return;
+        }
+
         String abi = chooseAbi(driverInfo);
         if (abi == null) {
             if (DEBUG) {
@@ -239,12 +348,6 @@
             }
             return;
         }
-        if (driverInfo.targetSdkVersion < Build.VERSION_CODES.O) {
-            // O drivers are restricted to the sphal linker namespace, so don't try to use
-            // packages unless they declare they're compatible with that restriction.
-            Log.w(TAG, "updated driver package is not known to be compatible with O");
-            return;
-        }
 
         StringBuilder sb = new StringBuilder();
         sb.append(driverInfo.nativeLibraryDir)
@@ -290,9 +393,39 @@
         return null;
     }
 
+    private static Set<String> loadWhitelist(Context context, String driverPackageName) {
+        String whitelistName = SystemProperties.get(PROPERTY_GFX_DRIVER_WHITELIST);
+        if (whitelistName == null || whitelistName.isEmpty()) {
+            return null;
+        }
+        try {
+            Context driverContext = context.createPackageContext(driverPackageName,
+                                                                 Context.CONTEXT_RESTRICTED);
+            AssetManager assets = driverContext.getAssets();
+            InputStream stream = assets.open(whitelistName);
+            BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+            Set<String> whitelist = new HashSet<>();
+            for (String line; (line = reader.readLine()) != null; ) {
+                whitelist.add(line);
+            }
+            return whitelist;
+        } catch (PackageManager.NameNotFoundException e) {
+            if (DEBUG) {
+                Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
+            }
+        } catch (IOException e) {
+            if (DEBUG) {
+                Log.w(TAG, "Failed to load whitelist driver package, abort.");
+            }
+        }
+        return null;
+    }
+
+    private static native int getCanLoadSystemLibraries();
     private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
     private static native void setDebugLayers(String layers);
     private static native void setDriverPath(String path);
     private static native void setAngleInfo(String path, String appPackage, String appPref,
-                                            boolean devOptIn);
+                                            boolean devOptIn, FileDescriptor rulesFd,
+                                            long rulesOffset, long rulesLength);
 }
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 20ca19b..c9c4205 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -388,10 +388,10 @@
 
     /**
      * Setup a new physical network.
-     * @param permission null if no permissions required to access this network.  PERMISSION_NETWORK
-     *                   or PERMISSION_SYSTEM to set respective permission.
+     * @param permission PERMISSION_NONE if no permissions required to access this network.
+     *                   PERMISSION_NETWORK or PERMISSION_SYSTEM to set respective permission.
      */
-    void createPhysicalNetwork(int netId, String permission);
+    void createPhysicalNetwork(int netId, int permission);
 
     /**
      * Setup a new VPN.
@@ -420,10 +420,10 @@
 
     /**
      * Set permission for a network.
-     * @param permission null to clear permissions. PERMISSION_NETWORK or PERMISSION_SYSTEM to set
-     *                   permission.
+     * @param permission PERMISSION_NONE to clear permissions.
+     *                   PERMISSION_NETWORK or PERMISSION_SYSTEM to set permission.
      */
-    void setNetworkPermission(int netId, String permission);
+    void setNetworkPermission(int netId, int permission);
 
     void setPermission(String permission, in int[] uids);
     void clearPermission(in int[] uids);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 8ea061e..e0b2c78 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -557,6 +557,7 @@
             ServiceType.FORCE_ALL_APPS_STANDBY,
             ServiceType.OPTIONAL_SENSORS,
             ServiceType.AOD,
+            ServiceType.QUICK_DOZE,
     })
     public @interface ServiceType {
         int NULL = 0;
@@ -586,6 +587,11 @@
          * Whether to disable non-essential sensors. (e.g. edge sensors.)
          */
         int OPTIONAL_SENSORS = 13;
+
+        /**
+         * Whether to go into Deep Doze as soon as the screen turns off or not.
+         */
+        int QUICK_DOZE = 15;
     }
 
     /**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0f64c45..379d28c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1003,11 +1003,38 @@
     public static final int PROC_OUT_LONG = 0x2000;
     /** @hide */
     public static final int PROC_OUT_FLOAT = 0x4000;
-    
-    /** @hide */
+
+    /**
+     * Read and parse a {@code proc} file in the given format.
+     *
+     * <p>The format is a list of integers, where every integer describes a variable in the file. It
+     * specifies how the variable is syntactically terminated (e.g. {@link Process#PROC_SPACE_TERM},
+     * {@link Process#PROC_TAB_TERM}, {@link Process#PROC_ZERO_TERM}).
+     *
+     * <p>If the variable should be parsed and returned to the caller, the termination type should
+     * be binary OR'd with the type of output (e.g. {@link Process#PROC_OUT_STRING}, {@link
+     * Process#PROC_OUT_LONG}, {@link Process#PROC_OUT_FLOAT}.
+     *
+     * <p>If the variable is wrapped in quotation marks it should be binary OR'd with {@link
+     * Process#PROC_QUOTES}. If the variable is wrapped in parentheses it should be binary OR'd with
+     * {@link Process#PROC_PARENS}.
+     *
+     * <p>If the variable is not formatted as a string and should be cast directly from characters
+     * to a long, the {@link Process#PROC_CHAR} integer should be binary OR'd.
+     *
+     * <p>If the terminating character can be repeated, the {@link Process#PROC_COMBINE} integer
+     * should be binary OR'd.
+     *
+     * @param file the path of the {@code proc} file to read
+     * @param format the format of the file
+     * @param outStrings the parsed {@code String}s from the file
+     * @param outLongs the parsed {@code long}s from the file
+     * @param outFloats the parsed {@code float}s from the file
+     * @hide
+     */
     public static final native boolean readProcFile(String file, int[] format,
             String[] outStrings, long[] outLongs, float[] outFloats);
-    
+
     /** @hide */
     public static final native boolean parseProcLine(byte[] buffer, int startIndex, 
             int endIndex, int[] format, String[] outStrings, long[] outLongs, float[] outFloats);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7ea2008..00b989e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -272,6 +272,10 @@
      *
      * Specifies if all users on the device are disallowed from enabling the
      * "Unknown Sources" setting, that allows installation of apps from unknown sources.
+     *
+     * This restriction can be enabled by the profile owner, in which case all accounts and
+     * profiles will be affected.
+     *
      * The default value is <code>false</code>.
      *
      * <p>Key for user restrictions.
@@ -983,6 +987,21 @@
     public static final String DISALLOW_PRINTING = "no_printing";
 
     /**
+     * Specifies whether the user is allowed to modify private DNS settings.
+     *
+     * <p>The default value is <code>false</code>.
+     *
+     * <p>This user restriction can only be applied by the Device Owner.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_PRIVATE_DNS =
+            "disallow_config_private_dns";
+
+    /**
      * Application restriction key that is used to indicate the pending arrival
      * of real restrictions for the app.
      *
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 50ca4ab..df771df 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1538,6 +1538,9 @@
      * @hide
      */
     public File translateAppToSystem(File file, String packageName) {
+        // We can only translate absolute paths
+        if (!file.isAbsolute()) return file;
+
         try {
             return new File(mStorageManager.translateAppToSystem(file.getAbsolutePath(),
                     packageName, mContext.getUserId()));
@@ -1553,6 +1556,9 @@
      * @hide
      */
     public File translateSystemToApp(File file, String packageName) {
+        // We can only translate absolute paths
+        if (!file.isAbsolute()) return file;
+
         try {
             return new File(mStorageManager.translateSystemToApp(file.getAbsolutePath(),
                     packageName, mContext.getUserId()));
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 8c40e0e..67e52aa 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -34,6 +34,7 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.ImageDecoder;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.media.ExifInterface;
@@ -53,6 +54,7 @@
 import android.system.Os;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Size;
 
 import libcore.io.IoUtils;
 
@@ -136,10 +138,11 @@
     public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
 
     /**
-     * Included in {@link AssetFileDescriptor#getExtras()} when returned
-     * thumbnail should be rotated.
+     * An extra number of degrees that an image should be rotated during the
+     * decode process to be presented correctly.
      *
-     * @see MediaStore.Images.ImageColumns#ORIENTATION
+     * @see AssetFileDescriptor#getExtras()
+     * @see android.provider.MediaStore.Images.ImageColumns#ORIENTATION
      */
     public static final String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
 
@@ -728,6 +731,8 @@
     public static final String EXTRA_PARENT_URI = "parentUri";
     /** {@hide} */
     public static final String EXTRA_URI = "uri";
+    /** {@hide} */
+    public static final String EXTRA_URI_PERMISSIONS = "uriPermissions";
 
     /**
      * @see #createWebLinkIntent(ContentResolver, Uri, Bundle)
@@ -1093,75 +1098,10 @@
 
     /** {@hide} */
     @UnsupportedAppUsage
-    public static Bitmap getDocumentThumbnail(
-            ContentProviderClient client, Uri documentUri, Point size, CancellationSignal signal)
-            throws RemoteException, IOException {
-        final Bundle openOpts = new Bundle();
-        openOpts.putParcelable(ContentResolver.EXTRA_SIZE, size);
-
-        AssetFileDescriptor afd = null;
-        Bitmap bitmap = null;
-        try {
-            afd = client.openTypedAssetFileDescriptor(documentUri, "image/*", openOpts, signal);
-
-            final FileDescriptor fd = afd.getFileDescriptor();
-            final long offset = afd.getStartOffset();
-
-            // Try seeking on the returned FD, since it gives us the most
-            // optimal decode path; otherwise fall back to buffering.
-            BufferedInputStream is = null;
-            try {
-                Os.lseek(fd, offset, SEEK_SET);
-            } catch (ErrnoException e) {
-                is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE);
-                is.mark(THUMBNAIL_BUFFER_SIZE);
-            }
-
-            // We requested a rough thumbnail size, but the remote size may have
-            // returned something giant, so defensively scale down as needed.
-            final BitmapFactory.Options opts = new BitmapFactory.Options();
-            opts.inJustDecodeBounds = true;
-            if (is != null) {
-                BitmapFactory.decodeStream(is, null, opts);
-            } else {
-                BitmapFactory.decodeFileDescriptor(fd, null, opts);
-            }
-
-            final int widthSample = opts.outWidth / size.x;
-            final int heightSample = opts.outHeight / size.y;
-
-            opts.inJustDecodeBounds = false;
-            opts.inSampleSize = Math.min(widthSample, heightSample);
-            if (is != null) {
-                is.reset();
-                bitmap = BitmapFactory.decodeStream(is, null, opts);
-            } else {
-                try {
-                    Os.lseek(fd, offset, SEEK_SET);
-                } catch (ErrnoException e) {
-                    e.rethrowAsIOException();
-                }
-                bitmap = BitmapFactory.decodeFileDescriptor(fd, null, opts);
-            }
-
-            // Transform the bitmap if requested. We use a side-channel to
-            // communicate the orientation, since EXIF thumbnails don't contain
-            // the rotation flags of the original image.
-            final Bundle extras = afd.getExtras();
-            final int orientation = (extras != null) ? extras.getInt(EXTRA_ORIENTATION, 0) : 0;
-            if (orientation != 0) {
-                final int width = bitmap.getWidth();
-                final int height = bitmap.getHeight();
-
-                final Matrix m = new Matrix();
-                m.setRotate(orientation, width / 2, height / 2);
-                bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, m, false);
-            }
-        } finally {
-            IoUtils.closeQuietly(afd);
-        }
-
-        return bitmap;
+    public static Bitmap getDocumentThumbnail(ContentProviderClient client, Uri documentUri,
+            Point size, CancellationSignal signal) throws IOException {
+        return ContentResolver.loadThumbnail(client, documentUri, Point.convert(size), signal,
+                ImageDecoder.ALLOCATOR_DEFAULT);
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 828fd73..57f33f0 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.UriPermission;
-import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
@@ -39,6 +38,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Environment;
+import android.os.OperationCanceledException;
 import android.os.RemoteException;
 import android.service.media.CameraPrewarmService;
 import android.util.ArrayMap;
@@ -46,9 +46,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 
-import libcore.io.IoUtils;
-
-import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -82,6 +79,11 @@
      */
     public static final String RETRANSLATE_CALL = "update_titles";
 
+    /** {@hide} */
+    public static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
+    /** {@hide} */
+    public static final String GET_MEDIA_URI_CALL = "get_media_uri";
+
     /**
      * This is for internal use by the media scanner only.
      * Name of the (optional) Uri parameter that determines whether to skip deleting
@@ -402,7 +404,16 @@
          * access.
          * <p>
          * Type: TEXT
+         *
+         * @deprecated Apps may not have filesystem permissions to directly
+         *             access this path. Instead of trying to open this path
+         *             directly, apps should use
+         *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+         *             to gain access. This value will always be {@code NULL}
+         *             for apps targeting
+         *             {@link android.os.Build.VERSION_CODES#Q} or higher.
          */
+        @Deprecated
         public static final String DATA = "_data";
 
         /**
@@ -641,6 +652,7 @@
      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
      * to be accessed elsewhere.
      */
+    @Deprecated
     private static class InternalThumbnails implements BaseColumns {
         /**
          * Currently outstanding thumbnail requests that can be cancelled.
@@ -654,13 +666,14 @@
          *
          * @see #cancelThumbnail(ContentResolver, Uri)
          */
+        @Deprecated
         static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
                 int kind, @Nullable BitmapFactory.Options opts) {
-            final Bundle openOpts = new Bundle();
+            final Point size;
             if (kind == ThumbnailConstants.MICRO_KIND) {
-                openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MICRO_SIZE);
+                size = ThumbnailConstants.MICRO_SIZE;
             } else if (kind == ThumbnailConstants.MINI_KIND) {
-                openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MINI_SIZE);
+                size = ThumbnailConstants.MINI_SIZE;
             } else {
                 throw new IllegalArgumentException("Unsupported kind: " + kind);
             }
@@ -674,9 +687,8 @@
                 }
             }
 
-            try (AssetFileDescriptor afd = cr.openTypedAssetFileDescriptor(uri,
-                    "image/*", openOpts, signal)) {
-                return BitmapFactory.decodeFileDescriptor(afd.getFileDescriptor(), null, opts);
+            try {
+                return cr.loadThumbnail(uri, Point.convert(size), signal);
             } catch (IOException e) {
                 Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
                 return null;
@@ -693,6 +705,7 @@
          * Only the original process which made the request can cancel their own
          * requests.
          */
+        @Deprecated
         static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
             synchronized (sPending) {
                 final CancellationSignal signal = sPending.get(uri);
@@ -936,9 +949,8 @@
         }
 
         /**
-         * This class allows developers to query and get two kinds of thumbnails:
-         * MINI_KIND: 512 x 384 thumbnail
-         * MICRO_KIND: 96 x 96 thumbnail
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Images} items.
          */
         public static class Thumbnails implements BaseColumns {
             public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
@@ -958,13 +970,19 @@
             }
 
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver
-             * @param origId original image id
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
+            @Deprecated
             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
                 final Uri uri = ContentUris.withAppendedId(
                         Images.Media.EXTERNAL_CONTENT_URI, origId);
@@ -972,51 +990,66 @@
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Return thumbnail representing a specific image item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image
-             *         associated with origId doesn't exist or memory is not enough.
+             * @param imageId the image item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long imageId, int kind,
                     BitmapFactory.Options options) {
                 final Uri uri = ContentUris.withAppendedId(
-                        Images.Media.EXTERNAL_CONTENT_URI, origId);
+                        Images.Media.EXTERNAL_CONTENT_URI, imageId);
                 return InternalThumbnails.getThumbnail(cr, uri, kind, options);
             }
 
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver
-             * @param origId original image id
-             * @param groupId the same groupId used in getThumbnail.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
+            @Deprecated
+            public static void cancelThumbnailRequest(ContentResolver cr, long origId,
+                    long groupId) {
                 cancelThumbnailRequest(cr, origId);
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Return thumbnail representing a specific image item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param groupId the id of group to which this request belongs
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image
-             *         associated with origId doesn't exist or memory is not enough.
+             * @param imageId the image item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long imageId, long groupId,
                     int kind, BitmapFactory.Options options) {
-                return getThumbnail(cr, origId, kind, options);
+                return getThumbnail(cr, imageId, kind, options);
             }
 
             /**
@@ -1059,7 +1092,16 @@
              * access.
              * <p>
              * Type: TEXT
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String DATA = "_data";
 
             /**
@@ -1509,7 +1551,16 @@
              * access.
              * <p>
              * Type: TEXT
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String DATA = "_data";
 
             /**
@@ -1790,7 +1841,16 @@
             /**
              * Cached album art.
              * <P>Type: TEXT</P>
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String ALBUM_ART = "album_art";
         }
 
@@ -2009,20 +2069,24 @@
         }
 
         /**
-         * This class allows developers to query and get two kinds of thumbnails:
-         * MINI_KIND: 512 x 384 thumbnail
-         * MICRO_KIND: 96 x 96 thumbnail
-         *
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Video} items.
          */
         public static class Thumbnails implements BaseColumns {
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver
-             * @param origId original video id
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
+            @Deprecated
             public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
                 final Uri uri = ContentUris.withAppendedId(
                         Video.Media.EXTERNAL_CONTENT_URI, origId);
@@ -2030,51 +2094,66 @@
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Return thumbnail representing a specific video item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image
-             *         associated with origId doesn't exist or memory is not enough.
+             * @param videoId the video item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind,
                     BitmapFactory.Options options) {
                 final Uri uri = ContentUris.withAppendedId(
-                        Video.Media.EXTERNAL_CONTENT_URI, origId);
+                        Video.Media.EXTERNAL_CONTENT_URI, videoId);
                 return InternalThumbnails.getThumbnail(cr, uri, kind, options);
             }
 
             /**
-             * This method checks if the thumbnails of the specified image (origId) has been created.
-             * It will be blocked until the thumbnails are generated.
+             * Cancel any outstanding {@link #getThumbnail} requests, causing
+             * them to return by throwing a {@link OperationCanceledException}.
+             * <p>
+             * This method has no effect on
+             * {@link ContentResolver#loadThumbnail} calls, since they provide
+             * their own {@link CancellationSignal}.
              *
-             * @param cr ContentResolver used to dispatch queries to MediaProvider.
-             * @param origId Original image id associated with thumbnail of interest.
-             * @param groupId the id of group to which this request belongs
-             * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
-             * @param options this is only used for MINI_KIND when decoding the Bitmap
-             * @return A Bitmap instance. It could be null if the original image associated with
-             *         origId doesn't exist or memory is not enough.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
-                    int kind, BitmapFactory.Options options) {
-                return getThumbnail(cr, origId, kind, options);
+            @Deprecated
+            public static void cancelThumbnailRequest(ContentResolver cr, long videoId,
+                    long groupId) {
+                cancelThumbnailRequest(cr, videoId);
             }
 
             /**
-             * This method cancels the thumbnail request so clients waiting for getThumbnail will be
-             * interrupted and return immediately. Only the original process which made the getThumbnail
-             * requests can cancel their own requests.
+             * Return thumbnail representing a specific video item. If a
+             * thumbnail doesn't exist, this method will block until it's
+             * generated. Callers are responsible for their own in-memory
+             * caching of returned values.
              *
-             * @param cr ContentResolver
-             * @param origId original video id
-             * @param groupId the same groupId used in getThumbnail.
+             * @param videoId the video item to obtain a thumbnail for.
+             * @param kind optimal thumbnail size desired.
+             * @return decoded thumbnail, or {@code null} if problem was
+             *         encountered.
+             * @deprecated Callers should migrate to using
+             *             {@link ContentResolver#loadThumbnail}, since it
+             *             offers richer control over requested thumbnail sizes
+             *             and cancellation behavior.
              */
-            public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
-                cancelThumbnailRequest(cr, origId);
+            @Deprecated
+            public static Bitmap getThumbnail(ContentResolver cr, long videoId, long groupId,
+                    int kind, BitmapFactory.Options options) {
+                return getThumbnail(cr, videoId, kind, options);
             }
 
             /**
@@ -2110,14 +2189,17 @@
             /**
              * Path to the thumbnail file on disk.
              * <p>
-             * Note that apps may not have filesystem permissions to directly
-             * access this path. Instead of trying to open this path directly,
-             * apps should use
-             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
-             * access.
-             * <p>
              * Type: TEXT
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#openFileDescriptor(Uri, String)}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
+            @Deprecated
             public static final String DATA = "_data";
 
             /**
@@ -2195,84 +2277,62 @@
     }
 
     /**
-     * Gets a URI backed by a {@link DocumentsProvider} that points to the same media
-     * file as the specified mediaUri. This allows apps who have permissions to access
-     * media files in Storage Access Framework to perform file operations through that
-     * on media files.
+     * Return a {@link DocumentsProvider} Uri that is an equivalent to the given
+     * {@link MediaStore} Uri.
      * <p>
-     * Note: this method doesn't grant any URI permission. Callers need to obtain
-     * permission before calling this method. One way to obtain permission is through
-     * a 3-step process:
-     * <ol>
-     *     <li>Call {@link android.os.storage.StorageManager#getStorageVolume(File)} to
-     *     obtain the {@link android.os.storage.StorageVolume} of a media file;</li>
+     * This allows apps with Storage Access Framework permissions to convert
+     * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
+     * to the same underlying item. Note that this method doesn't grant any new
+     * permissions; callers must already hold permissions obtained with
+     * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
      *
-     *     <li>Invoke the intent returned by
-     *     {@link android.os.storage.StorageVolume#createAccessIntent(String)} to
-     *     obtain the access of the volume or one of its specific subdirectories;</li>
-     *
-     *     <li>Check whether permission is granted and take persistent permission.</li>
-     * </ol>
-     * @param mediaUri the media URI which document URI is requested
-     * @return the document URI
+     * @param mediaUri The {@link MediaStore} Uri to convert.
+     * @return An equivalent {@link DocumentsProvider} Uri. Returns {@code null}
+     *         if no equivalent was found.
+     * @see #getMediaUri(Context, Uri)
      */
     public static Uri getDocumentUri(Context context, Uri mediaUri) {
+        final ContentResolver resolver = context.getContentResolver();
+        final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
 
-        try {
-            final ContentResolver resolver = context.getContentResolver();
-
-            final String path = getFilePath(resolver, mediaUri);
-            final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
-
-            return getDocumentUri(resolver, path, uriPermissions);
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+            final Bundle in = new Bundle();
+            in.putParcelable(DocumentsContract.EXTRA_URI, mediaUri);
+            in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
+            final Bundle out = client.call(GET_DOCUMENT_URI_CALL, null, in);
+            return out.getParcelable(DocumentsContract.EXTRA_URI);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
-    private static String getFilePath(ContentResolver resolver, Uri mediaUri)
-            throws RemoteException {
+    /**
+     * Return a {@link MediaStore} Uri that is an equivalent to the given
+     * {@link DocumentsProvider} Uri.
+     * <p>
+     * This allows apps with Storage Access Framework permissions to convert
+     * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
+     * to the same underlying item. Note that this method doesn't grant any new
+     * permissions; callers must already hold permissions obtained with
+     * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
+     *
+     * @param documentUri The {@link DocumentsProvider} Uri to convert.
+     * @return An equivalent {@link MediaStore} Uri. Returns {@code null} if no
+     *         equivalent was found.
+     * @see #getDocumentUri(Context, Uri)
+     */
+    public static Uri getMediaUri(Context context, Uri documentUri) {
+        final ContentResolver resolver = context.getContentResolver();
+        final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
 
-        try (ContentProviderClient client =
-                     resolver.acquireUnstableContentProviderClient(AUTHORITY)) {
-            final Cursor c = client.query(
-                    mediaUri,
-                    new String[]{ MediaColumns.DATA },
-                    null, /* selection */
-                    null, /* selectionArg */
-                    null /* sortOrder */);
-
-            final String path;
-            try {
-                if (c.getCount() == 0) {
-                    throw new IllegalStateException("Not found media file under URI: " + mediaUri);
-                }
-
-                if (!c.moveToFirst()) {
-                    throw new IllegalStateException("Failed to move cursor to the first item.");
-                }
-
-                path = c.getString(0);
-            } finally {
-                IoUtils.closeQuietly(c);
-            }
-
-            return path;
-        }
-    }
-
-    private static Uri getDocumentUri(
-            ContentResolver resolver, String path, List<UriPermission> uriPermissions)
-            throws RemoteException {
-
-        try (ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
-                DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY)) {
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
             final Bundle in = new Bundle();
-            in.putParcelableList(
-                    DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY + ".extra.uriPermissions",
-                    uriPermissions);
-            final Bundle out = client.call("getDocumentId", path, in);
+            in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+            in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
+            final Bundle out = client.call(GET_MEDIA_URI_CALL, null, in);
             return out.getParcelable(DocumentsContract.EXTRA_URI);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index acb7577..80e8f78 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1473,12 +1473,13 @@
      * <p> If an user action is disabled by policy, this dialog can be triggered to let
      * the user know about this.
      * <p>
-     * Input: Nothing.
+     * Input: {@link Intent#EXTRA_USER}: The user of the admin.
      * <p>
      * Output: Nothing.
      *
      * @hide
      */
+    // Intent#EXTRA_USER_ID can also be used
     @SystemApi
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS
@@ -1613,6 +1614,11 @@
     public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
 
     /**
+     * @hide - Private call() method on SettingsProvider to read from 'config' table.
+     */
+    public static final String CALL_METHOD_GET_CONFIG = "GET_config";
+
+    /**
      * @hide - Specifies that the caller of the fast-path call()-based flow tracks
      * the settings generation in order to cache values locally. If this key is
      * mapped to a <code>null</code> string extra in the request bundle, the response
@@ -1671,9 +1677,15 @@
     /** @hide - Private call() method to write to 'global' table */
     public static final String CALL_METHOD_PUT_GLOBAL= "PUT_global";
 
+    /** @hide - Private call() method to write to 'configuration' table */
+    public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
+
     /** @hide - Private call() method to reset to defaults the 'global' table */
     public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
 
+    /** @hide - Private call() method to reset to defaults the 'configuration' table */
+    public static final String CALL_METHOD_RESET_CONFIG = "RESET_config";
+
     /** @hide - Private call() method to reset to defaults the 'secure' table */
     public static final String CALL_METHOD_RESET_SECURE = "RESET_secure";
 
@@ -10895,6 +10907,7 @@
          * idle_pending_to                  (long)
          * max_idle_pending_to              (long)
          * idle_pending_factor              (float)
+         * quick_doze_delay_to              (long)
          * idle_to                          (long)
          * max_idle_to                      (long)
          * idle_factor                      (float)
@@ -10928,6 +10941,11 @@
          * gps_mode                          (int)
          * adjust_brightness_disabled        (boolean)
          * adjust_brightness_factor          (float)
+         * force_all_apps_standby            (boolean)
+         * force_background_check            (boolean)
+         * optional_sensors_disabled         (boolean)
+         * aod_disabled                      (boolean)
+         * quick_doze_enabled                (boolean)
          * </pre>
          * @hide
          * @see com.android.server.power.BatterySaverPolicy
@@ -11631,6 +11649,12 @@
         public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
 
         /**
+         * Addition app for GPU layer discovery
+         * @hide
+         */
+        public static final String GPU_DEBUG_LAYER_APP = "gpu_debug_layer_app";
+
+        /**
          * Control whether the process CPU usage meter should be shown.
          *
          * @deprecated This functionality is no longer available as of
@@ -12363,6 +12387,28 @@
                 "sms_access_restriction_enabled";
 
         /**
+         * If set to 1, an app must have the READ_PRIVILEGED_PHONE_STATE permission (or be a device
+         * / profile owner with the READ_PHONE_STATE permission) to access device identifiers.
+         *
+         * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+         *
+         * @hide
+         */
+        public static final String PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED =
+                "privileged_device_identifier_check_enabled";
+
+        /**
+         * If set to 1, an app that is targeting Q and does not meet the new requirements to access
+         * device identifiers will receive a SecurityException.
+         *
+         * STOPSHIP: Remove this once we ship with the new device identifier check enabled.
+         *
+         * @hide
+         */
+        public static final String PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED =
+                "privileged_device_identifier_target_q_behavior_enabled";
+
+        /**
          * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
          * and restoring to lower version of platform API will be skipped.
          *
@@ -13432,6 +13478,112 @@
     }
 
     /**
+     * Configuration system settings, containing settings which are applied identically for all
+     * defined users. Only Android can read these and only a specific configuration service can
+     * write these.
+     *
+     * @hide
+     */
+    public static final class Config extends NameValueTable {
+        /**
+         * The content:// style URL for the config table.
+         *
+         * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a
+         *     System API.
+         */
+        private static final Uri CONTENT_URI =
+                Uri.parse("content://" + AUTHORITY + "/config");
+
+        private static final ContentProviderHolder sProviderHolder =
+                new ContentProviderHolder(CONTENT_URI);
+
+        // Populated lazily, guarded by class object:
+        private static final NameValueCache sNameValueCache = new NameValueCache(
+                CONTENT_URI,
+                CALL_METHOD_GET_CONFIG,
+                CALL_METHOD_PUT_CONFIG,
+                sProviderHolder);
+
+        /**
+         * Look up a name in the database.
+         * @param resolver to access the database with
+         * @param name to look up in the table
+         * @return the corresponding value, or null if not present
+         *
+         * @hide
+         */
+        // TODO(b/117663715): require a new read permission
+        static String getString(ContentResolver resolver, String name) {
+            return sNameValueCache.getStringForUser(resolver, name, resolver.getUserId());
+        }
+
+        /**
+         * Store a name/value pair into the database.
+         * <p>
+         * The method takes an optional tag to associate with the setting which can be used to clear
+         * only settings made by your package and associated with this tag by passing the tag to
+         * {@link #resetToDefaults(ContentResolver, String)}. The value of this setting can be
+         * overridden by future calls to this or other put methods, and the tag provided in those
+         * calls, which may be null, will override the tag provided in this call. Any call to a put
+         * method which does not accept a tag will effectively set the tag to null.
+         * </p><p>
+         * Also the method takes an argument whether to make the value the default for this setting.
+         * If the system already specified a default value, then the one passed in here will
+         * <strong>not</strong> be set as the default.
+         * </p>
+         *
+         * @param resolver to access the database with.
+         * @param name to store.
+         * @param value to associate with the name.
+         * @param tag to associated with the setting.
+         * @param makeDefault whether to make the value the default one.
+         * @return true if the value was set, false on database errors.
+         *
+         * @see #resetToDefaults(ContentResolver, String)
+         *
+         * @hide
+         */
+        // TODO(b/117663715): require a new write permission restricted to a single source
+        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+        static boolean putString(@NonNull ContentResolver resolver,
+                @NonNull String name, @Nullable String value, @Nullable String tag,
+                boolean makeDefault) {
+            return sNameValueCache.putStringForUser(resolver, name, value, tag, makeDefault,
+                    resolver.getUserId());
+        }
+
+        /**
+         * Reset the settings to their defaults. This would reset <strong>only</strong> settings set
+         * by the caller's package. Passing in the optional tag will reset only settings changed by
+         * your package and associated with this tag.
+         *
+         * @param resolver Handle to the content resolver.
+         * @param tag Optional tag which should be associated with the settings to reset.
+         *
+         * @see #putString(ContentResolver, String, String, String, boolean)
+         *
+         * @hide
+         */
+        // TODO(b/117663715): require a new write permission restricted to a single source
+        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+        static void resetToDefaults(@NonNull ContentResolver resolver,
+                @Nullable String tag) {
+            try {
+                Bundle arg = new Bundle();
+                arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
+                if (tag != null) {
+                    arg.putString(CALL_METHOD_TAG_KEY, tag);
+                }
+                arg.putInt(CALL_METHOD_RESET_MODE_KEY, RESET_MODE_PACKAGE_DEFAULTS);
+                IContentProvider cp = sProviderHolder.getProvider(resolver);
+                cp.call(resolver.getPackageName(), CALL_METHOD_RESET_CONFIG, null, arg);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI, e);
+            }
+        }
+    }
+
+    /**
      * User-defined bookmarks and shortcuts.  The target of each bookmark is an
      * Intent URL, allowing it to be either a web page or a particular
      * application activity.
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index c748c87..035b226 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -113,19 +113,6 @@
     }
 
     /**
-     * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
-     *
-     * See implementation for binary key format.
-     *
-     * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
-     * @removed
-     */
-    @Deprecated
-    public @NonNull byte[] getTrustedHardwarePublicKey() {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
      */
     public @NonNull CertPath getTrustedHardwareCertPath() {
@@ -223,18 +210,6 @@
         }
 
         /**
-         * Sets public key used to encrypt recovery blob.
-         *
-         * @param publicKey The public key
-         * @return This builder.
-         * @removed Use {@link #setTrustedHardwareCertPath} instead.
-         */
-        @Deprecated
-        public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
-            throw new UnsupportedOperationException();
-        }
-
-        /**
          * Sets CertPath used to validate the trusted hardware public key. The CertPath should
          * contain a certificate of the trusted hardware public key and any necessary intermediate
          * certificates.
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 70054fc..31a5962 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -23,7 +23,6 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
@@ -301,18 +300,6 @@
     }
 
     /**
-     * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public void initRecoveryService(
-            @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
-            throws CertificateException, InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Initializes the recovery service for the calling application. The detailed steps should be:
      * <ol>
      *     <li>Parse {@code signatureFile} to get relevant information.
@@ -363,16 +350,6 @@
     }
 
     /**
-     * @deprecated Use {@link #getKeyChainSnapshot()}
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public @Nullable KeyChainSnapshot getRecoveryData() throws InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Returns data necessary to store all recoverable keys. Key material is
      * encrypted with user secret and recovery public key.
      *
@@ -440,17 +417,6 @@
     }
 
     /**
-     * @deprecated Use {@link #getAliases()}.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public List<String> getAliases(@Nullable String packageName)
-            throws InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Returns a list of aliases of keys belonging to the application.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -466,18 +432,6 @@
     }
 
     /**
-     * @deprecated Use {@link #setRecoveryStatus(String, int)}
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public void setRecoveryStatus(
-            @NonNull String packageName, String alias, int status)
-            throws NameNotFoundException, InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Sets the recovery status for given key. It is used to notify the keystore that the key was
      * successfully stored on the server or that there was an error. An application can check this
      * value using {@link #getRecoveryStatus(String, String)}.
@@ -501,17 +455,6 @@
     }
 
     /**
-     * @deprecated Use {@link #getRecoveryStatus(String)}.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public int getRecoveryStatus(String packageName, String alias)
-            throws InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Returns the recovery status for the key with the given {@code alias}.
      *
      * <ul>
@@ -584,39 +527,6 @@
     }
 
     /**
-     * Deprecated.
-     * Generates a AES256/GCM/NoPADDING key called {@code alias} and loads it into the recoverable
-     * key store. Returns the raw material of the key.
-     *
-     * @param alias The key alias.
-     * @param account The account associated with the key
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
-     *     to generate recoverable keys, as the snapshots are encrypted using a key derived from the
-     *     lock screen.
-     * @deprecated Use {@link #generateKey(String)}
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
-            throws InternalRecoveryServiceException, LockScreenRequiredException {
-        throw new UnsupportedOperationException("Operation is not supported, use generateKey");
-    }
-
-    /**
-     * @deprecated Use {@link #generateKey(String)}.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public Key generateKey(@NonNull String alias, byte[] account)
-            throws InternalRecoveryServiceException, LockScreenRequiredException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Generates a recoverable key with the given {@code alias}.
      *
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 3bb6421..42e7182 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -78,36 +78,6 @@
     }
 
     /**
-     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    @NonNull public byte[] start(
-            @NonNull byte[] verifierPublicKey,
-            @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge,
-            @NonNull List<KeyChainProtectionParams> secrets)
-            throws CertificateException, InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    @NonNull public byte[] start(
-            @NonNull CertPath verifierCertPath,
-            @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge,
-            @NonNull List<KeyChainProtectionParams> secrets)
-            throws CertificateException, InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Starts a recovery session and returns a blob with proof of recovery secret possession.
      * The method generates a symmetric key for a session, which trusted remote device can use to
      * return recovery key.
@@ -162,20 +132,6 @@
     }
 
     /**
-     * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
-     * @removed
-     */
-    @Deprecated
-    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public Map<String, byte[]> recoverKeys(
-            @NonNull byte[] recoveryKeyBlob,
-            @NonNull List<WrappedApplicationKey> applicationKeys)
-            throws SessionExpiredException, DecryptionFailedException,
-            InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * Imports key chain snapshot recovered from a remote vault.
      *
      * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index 187a671..ae4448f 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -75,15 +75,6 @@
         }
 
         /**
-         * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
-         * @removed
-         */
-        @Deprecated
-        public Builder setAccount(@NonNull byte[] account) {
-            throw new UnsupportedOperationException();
-        }
-
-        /**
          * Sets key material encrypted by recovery key.
          *
          * @param encryptedKeyMaterial The key material
@@ -133,15 +124,6 @@
         return mEncryptedKeyMaterial;
     }
 
-    /**
-     * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
-     * @removed
-     */
-    @Deprecated
-    public @NonNull byte[] getAccount() {
-        throw new UnsupportedOperationException();
-    }
-
     public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
             new Parcelable.Creator<WrappedApplicationKey>() {
                 public WrappedApplicationKey createFromParcel(Parcel in) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index d7fe15d..b8e0387 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -737,7 +737,8 @@
      * <p>This method will throw a security exception if you don't have access to notifications
      * for the given user.</p>
      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
-     * device} in order to use this method.
+     * device} or be the {@link NotificationAssistantService notification assistant} in order to
+     * use this method.
      *
      * @param pkg The package to retrieve channels for.
      */
@@ -760,7 +761,8 @@
      * <p>This method will throw a security exception if you don't have access to notifications
      * for the given user.</p>
      * <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
-     * device} in order to use this method.
+     * device} or be the {@link NotificationAssistantService notification assistant} in order to
+     * use this method.
      *
      * @param pkg The package to retrieve channel groups for.
      */
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 10d7911..ec63cd9 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -1112,7 +1112,6 @@
 
         @Override
         protected void playImpl() {
-            dispatchOnStart();
             super.playImpl();
             try {
               mFileOutputStream.close();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c89617f..3ab8a0a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -23,6 +23,7 @@
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.graphics.text.LineBreaker;
 import android.text.method.TextKeyListener;
 import android.text.style.AlignmentSpan;
 import android.text.style.LeadingMarginSpan;
@@ -50,9 +51,9 @@
 public abstract class Layout {
     /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
-            NativeLineBreaker.BREAK_STRATEGY_SIMPLE,
-            NativeLineBreaker.BREAK_STRATEGY_HIGH_QUALITY,
-            NativeLineBreaker.BREAK_STRATEGY_BALANCED
+            LineBreaker.BREAK_STRATEGY_SIMPLE,
+            LineBreaker.BREAK_STRATEGY_HIGH_QUALITY,
+            LineBreaker.BREAK_STRATEGY_BALANCED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BreakStrategy {}
@@ -63,20 +64,19 @@
      * before it (which yields a more consistent user experience when editing), but layout may not
      * be the highest quality.
      */
-    public static final int BREAK_STRATEGY_SIMPLE = NativeLineBreaker.BREAK_STRATEGY_SIMPLE;
+    public static final int BREAK_STRATEGY_SIMPLE = LineBreaker.BREAK_STRATEGY_SIMPLE;
 
     /**
      * Value for break strategy indicating high quality line breaking, including automatic
      * hyphenation and doing whole-paragraph optimization of line breaks.
      */
-    public static final int BREAK_STRATEGY_HIGH_QUALITY =
-            NativeLineBreaker.BREAK_STRATEGY_HIGH_QUALITY;
+    public static final int BREAK_STRATEGY_HIGH_QUALITY = LineBreaker.BREAK_STRATEGY_HIGH_QUALITY;
 
     /**
      * Value for break strategy indicating balanced line breaking. The breaks are chosen to
      * make all lines as close to the same length as possible, including automatic hyphenation.
      */
-    public static final int BREAK_STRATEGY_BALANCED = NativeLineBreaker.BREAK_STRATEGY_BALANCED;
+    public static final int BREAK_STRATEGY_BALANCED = LineBreaker.BREAK_STRATEGY_BALANCED;
 
     /** @hide */
     @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
@@ -94,32 +94,29 @@
      * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used
      * as suggestions for potential line breaks.
      */
-    public static final int HYPHENATION_FREQUENCY_NONE =
-            NativeLineBreaker.HYPHENATION_FREQUENCY_NONE;
+    public static final int HYPHENATION_FREQUENCY_NONE = LineBreaker.HYPHENATION_FREQUENCY_NONE;
 
     /**
      * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which
      * is a conservative default. Useful for informal cases, such as short sentences or chat
      * messages.
      */
-    public static final int HYPHENATION_FREQUENCY_NORMAL =
-            NativeLineBreaker.HYPHENATION_FREQUENCY_NORMAL;
+    public static final int HYPHENATION_FREQUENCY_NORMAL = LineBreaker.HYPHENATION_FREQUENCY_NORMAL;
 
     /**
      * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical
      * in typography. Useful for running text and where it's important to put the maximum amount of
      * text in a screen with limited space.
      */
-    public static final int HYPHENATION_FREQUENCY_FULL =
-            NativeLineBreaker.HYPHENATION_FREQUENCY_FULL;
+    public static final int HYPHENATION_FREQUENCY_FULL = LineBreaker.HYPHENATION_FREQUENCY_FULL;
 
     private static final ParagraphStyle[] NO_PARA_SPANS =
         ArrayUtils.emptyArray(ParagraphStyle.class);
 
     /** @hide */
     @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
-            NativeLineBreaker.JUSTIFICATION_MODE_NONE,
-            NativeLineBreaker.JUSTIFICATION_MODE_INTER_WORD
+            LineBreaker.JUSTIFICATION_MODE_NONE,
+            LineBreaker.JUSTIFICATION_MODE_INTER_WORD
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface JustificationMode {}
@@ -127,13 +124,13 @@
     /**
      * Value for justification mode indicating no justification.
      */
-    public static final int JUSTIFICATION_MODE_NONE = NativeLineBreaker.JUSTIFICATION_MODE_NONE;
+    public static final int JUSTIFICATION_MODE_NONE = LineBreaker.JUSTIFICATION_MODE_NONE;
 
     /**
      * Value for justification mode indicating the text is justified by stretching word spacing.
      */
     public static final int JUSTIFICATION_MODE_INTER_WORD =
-            NativeLineBreaker.JUSTIFICATION_MODE_INTER_WORD;
+            LineBreaker.JUSTIFICATION_MODE_INTER_WORD;
 
     /*
      * Line spacing multiplier for default line spacing.
@@ -1809,6 +1806,7 @@
         }
 
     }
+
     /**
      * Fills in the specified Path with a representation of a cursor
      * at the specified offset.  This will often be a vertical line
@@ -1824,7 +1822,6 @@
 
         boolean clamped = shouldClampCursor(line);
         float h1 = getPrimaryHorizontal(point, clamped) - 0.5f;
-        float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1;
 
         int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
                    TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
@@ -1842,34 +1839,24 @@
 
         if (h1 < 0.5f)
             h1 = 0.5f;
-        if (h2 < 0.5f)
-            h2 = 0.5f;
 
-        if (Float.compare(h1, h2) == 0) {
-            dest.moveTo(h1, top);
-            dest.lineTo(h1, bottom);
-        } else {
-            dest.moveTo(h1, top);
-            dest.lineTo(h1, (top + bottom) >> 1);
-
-            dest.moveTo(h2, (top + bottom) >> 1);
-            dest.lineTo(h2, bottom);
-        }
+        dest.moveTo(h1, top);
+        dest.lineTo(h1, bottom);
 
         if (caps == 2) {
-            dest.moveTo(h2, bottom);
-            dest.lineTo(h2 - dist, bottom + dist);
-            dest.lineTo(h2, bottom);
-            dest.lineTo(h2 + dist, bottom + dist);
+            dest.moveTo(h1, bottom);
+            dest.lineTo(h1 - dist, bottom + dist);
+            dest.lineTo(h1, bottom);
+            dest.lineTo(h1 + dist, bottom + dist);
         } else if (caps == 1) {
-            dest.moveTo(h2, bottom);
-            dest.lineTo(h2 - dist, bottom + dist);
+            dest.moveTo(h1, bottom);
+            dest.lineTo(h1 - dist, bottom + dist);
 
-            dest.moveTo(h2 - dist, bottom + dist - 0.5f);
-            dest.lineTo(h2 + dist, bottom + dist - 0.5f);
+            dest.moveTo(h1 - dist, bottom + dist - 0.5f);
+            dest.lineTo(h1 + dist, bottom + dist - 0.5f);
 
-            dest.moveTo(h2 + dist, bottom + dist);
-            dest.lineTo(h2, bottom);
+            dest.moveTo(h1 + dist, bottom + dist);
+            dest.lineTo(h1, bottom);
         }
 
         if (fn == 2) {
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 9bf8cd2..f9370a8 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.text.MeasuredText;
 import android.text.AutoGrowArray.ByteArray;
 import android.text.AutoGrowArray.FloatArray;
 import android.text.AutoGrowArray.IntArray;
@@ -121,7 +122,7 @@
     private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
 
     // The native MeasuredParagraph.
-    private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;
+    private @Nullable MeasuredText mMeasuredText;
 
     // Following two objects are for avoiding object allocation.
     private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -149,7 +150,7 @@
         mWidths.clear();
         mFontMetrics.clear();
         mSpanEndCache.clear();
-        mNativeMeasuredParagraph = null;
+        mMeasuredText = null;
     }
 
     /**
@@ -245,8 +246,8 @@
      * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      * Returns null in other cases.
      */
-    public NativeMeasuredParagraph getNativeMeasuredParagraph() {
-        return mNativeMeasuredParagraph;
+    public MeasuredText getMeasuredText() {
+        return mMeasuredText;
     }
 
     /**
@@ -259,7 +260,7 @@
      * @param end the exclusive end offset of the target region in the text
      */
     public float getWidth(int start, int end) {
-        if (mNativeMeasuredParagraph == null) {
+        if (mMeasuredText == null) {
             // We have result in Java.
             final float[] widths = mWidths.getRawArray();
             float r = 0.0f;
@@ -269,7 +270,7 @@
             return r;
         } else {
             // We have result in native.
-            return mNativeMeasuredParagraph.getWidth(start, end);
+            return mMeasuredText.getWidth(start, end);
         }
     }
 
@@ -281,7 +282,7 @@
      */
     public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Rect bounds) {
-        mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
+        mMeasuredText.getBounds(start, end, bounds);
     }
 
     /**
@@ -290,7 +291,7 @@
      * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      */
     public float getCharWidthAt(@IntRange(from = 0) int offset) {
-        return mNativeMeasuredParagraph.getCharWidthAt(offset);
+        return mMeasuredText.getCharWidthAt(offset);
     }
 
     /**
@@ -391,12 +392,13 @@
             @Nullable MeasuredParagraph recycle) {
         final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
         mt.resetAndAnalyzeBidi(text, start, end, textDir);
-        final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
+        final MeasuredText.Builder builder = new MeasuredText.Builder(mt.mCopiedBuffer);
+        builder.setComputeHyphenation(computeHyphenation);
+        builder.setComputeLayout(computeLayout);
         if (mt.mTextLength == 0) {
             // Need to build empty native measured text for StaticLayout.
             // TODO: Stop creating empty measured text for empty lines.
-            mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
-                        computeLayout);
+            mt.mMeasuredText = builder.build();
         } else {
             if (mt.mSpanned == null) {
                 // No style change by MetricsAffectingSpan. Just measure all text.
@@ -417,8 +419,7 @@
                     mt.mSpanEndCache.append(spanEnd);
                 }
             }
-            mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
-                    computeLayout);
+            mt.mMeasuredText = builder.build();
         }
 
         return mt;
@@ -490,7 +491,7 @@
     private void applyReplacementRun(@NonNull ReplacementSpan replacement,
                                      @IntRange(from = 0) int start,  // inclusive, in copied buffer
                                      @IntRange(from = 0) int end,  // exclusive, in copied buffer
-                                     @Nullable NativeMeasuredParagraph.Builder builder) {
+                                     @Nullable MeasuredText.Builder builder) {
         // Use original text. Shouldn't matter.
         // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
         //       backward compatibility? or Should we initialize them for getFontMetricsInt?
@@ -504,13 +505,13 @@
             }
             mWholeWidth += width;
         } else {
-            builder.addReplacementRun(mCachedPaint, start, end, width);
+            builder.appendReplacementRun(mCachedPaint, end - start, width);
         }
     }
 
     private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
                                @IntRange(from = 0) int end,  // exclusive, in copied buffer
-                               @Nullable NativeMeasuredParagraph.Builder builder) {
+                               @Nullable MeasuredText.Builder builder) {
 
         if (mLtrWithoutBidi) {
             // If the whole text is LTR direction, just apply whole region.
@@ -519,7 +520,7 @@
                         mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
                         mWidths.getRawArray(), start);
             } else {
-                builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
+                builder.appendStyleRun(mCachedPaint, end - start, false /* isRtl */);
             }
         } else {
             // If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -535,7 +536,7 @@
                                 mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
                                 isRtl, mWidths.getRawArray(), levelStart);
                     } else {
-                        builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
+                        builder.appendStyleRun(mCachedPaint, levelEnd - levelStart, isRtl);
                     }
                     if (levelEnd == end) {
                         break;
@@ -552,7 +553,7 @@
             @Nullable MetricAffectingSpan[] spans,
             @IntRange(from = 0) int start,  // inclusive, in original text buffer
             @IntRange(from = 0) int end,  // exclusive, in original text buffer
-            @Nullable NativeMeasuredParagraph.Builder builder) {
+            @Nullable MeasuredText.Builder builder) {
         mCachedPaint.set(paint);
         // XXX paint should not have a baseline shift, but...
         mCachedPaint.baselineShift = 0;
@@ -658,6 +659,6 @@
      * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
      */
     public @IntRange(from = 0) int getMemoryUsage() {
-        return mNativeMeasuredParagraph.getMemoryUsage();
+        return mMeasuredText.getMemoryUsage();
     }
 }
diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java
deleted file mode 100644
index bfdccca..0000000
--- a/core/java/android/text/NativeMeasuredParagraph.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.graphics.Paint;
-import android.graphics.Rect;
-
-import dalvik.annotation.optimization.CriticalNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-/**
- * A native implementation of measured paragraph.
- * TODO: Consider to make this class public.
- * @hide
- */
-public class NativeMeasuredParagraph {
-    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-            NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
-    private long mNativePtr;
-    private @NonNull char[] mChars;
-
-    // Use builder instead.
-    private NativeMeasuredParagraph(long ptr, @NonNull char[] chars) {
-        mNativePtr = ptr;
-        mChars = chars;
-    }
-
-    /**
-     * Returns a characters of this paragraph.
-     */
-    public char[] getChars() {
-        return mChars;
-    }
-
-    /**
-     * Returns a width of the given region
-     */
-    public float getWidth(int start, int end) {
-        return nGetWidth(mNativePtr, start, end);
-    }
-
-    /**
-     * Returns a memory usage of the native object.
-     */
-    public int getMemoryUsage() {
-        return nGetMemoryUsage(mNativePtr);
-    }
-
-    /**
-     * Fills the boundary box of the given region
-     */
-    public void getBounds(char[] buf, int start, int end, Rect rect) {
-        nGetBounds(mNativePtr, buf, start, end, rect);
-    }
-
-    /**
-     * Returns the width of the character at the given offset
-     */
-    public float getCharWidthAt(int offset) {
-        return nGetCharWidthAt(mNativePtr, offset);
-    }
-
-    /**
-     * Returns a native pointer of the underlying native object.
-     */
-    public long getNativePtr() {
-        return mNativePtr;
-    }
-
-    @CriticalNative
-    private static native float nGetWidth(/* Non Zero */ long nativePtr,
-                                         @IntRange(from = 0) int start,
-                                         @IntRange(from = 0) int end);
-
-    @CriticalNative
-    private static native /* Non Zero */ long nGetReleaseFunc();
-
-    @CriticalNative
-    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
-
-    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
-            Rect rect);
-
-    @CriticalNative
-    private static native float nGetCharWidthAt(long nativePtr, int offset);
-
-    /**
-     * A builder for the NativeMeasuredParagraph
-     */
-    public static class Builder {
-        private final long mNativePtr;
-
-        public Builder() {
-            mNativePtr = nInitBuilder();
-        }
-
-        /**
-         * Apply styles to given range
-         */
-        public void addStyleRun(@NonNull Paint paint, int start, int end, boolean isRtl) {
-            nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
-        }
-
-        /**
-         * Tells native that the given range is replaced with the object of given width.
-         */
-        public void addReplacementRun(@NonNull Paint paint, int start, int end, float width) {
-            nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
-        }
-
-        /**
-         * Build the NativeMeasuredParagraph
-         */
-        public NativeMeasuredParagraph build(char[] text, boolean computeHyphenation,
-                boolean computeLayout) {
-            try {
-                long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation,
-                        computeLayout);
-                NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr, text);
-                sRegistry.registerNativeAllocation(res, ptr);
-                return res;
-            } finally {
-                nFreeBuilder(mNativePtr);
-            }
-        }
-
-        private static native /* Non Zero */ long nInitBuilder();
-
-        /**
-         * Apply style to make native measured text.
-         *
-         * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
-         * @param paintPtr The native paint pointer to be applied.
-         * @param start The start offset in the copied buffer.
-         * @param end The end offset in the copied buffer.
-         * @param isRtl True if the text is RTL.
-         */
-        private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
-                                                /* Non Zero */ long paintPtr,
-                                                @IntRange(from = 0) int start,
-                                                @IntRange(from = 0) int end,
-                                                boolean isRtl);
-        /**
-         * Apply ReplacementRun to make native measured text.
-         *
-         * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
-         * @param paintPtr The native paint pointer to be applied.
-         * @param start The start offset in the copied buffer.
-         * @param end The end offset in the copied buffer.
-         * @param width The width of the replacement.
-         */
-        private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
-                                                      /* Non Zero */ long paintPtr,
-                                                      @IntRange(from = 0) int start,
-                                                      @IntRange(from = 0) int end,
-                                                      @FloatRange(from = 0) float width);
-
-        private static native long nBuildNativeMeasuredParagraph(
-                /* Non Zero */ long nativeBuilderPtr,
-                @NonNull char[] text,
-                boolean computeHyphenation,
-                boolean computeLayout);
-
-        private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
-    }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d2f0853..2cf0262 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Paint;
+import android.graphics.text.LineBreaker;
 import android.os.Build;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
@@ -55,7 +56,7 @@
      *
      *   - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
      *     native.
-     *   - Run NativeLineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
+     *   - Run LineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
      *
      * After all paragraphs, call finish() to release expensive buffers.
      */
@@ -634,7 +635,7 @@
             indents = null;
         }
 
-        final NativeLineBreaker lineBreaker = new NativeLineBreaker.Builder()
+        final LineBreaker lineBreaker = new LineBreaker.Builder()
                 .setBreakStrategy(b.mBreakStrategy)
                 .setHyphenationFrequency(b.mHyphenationFrequency)
                 // TODO: Support more justification mode, e.g. letter spacing, stretching.
@@ -642,8 +643,8 @@
                 .setIndents(indents)
                 .build();
 
-        NativeLineBreaker.ParagraphConstraints constraints =
-                new NativeLineBreaker.ParagraphConstraints();
+        LineBreaker.ParagraphConstraints constraints =
+                new LineBreaker.ParagraphConstraints();
 
         PrecomputedText.ParagraphInfo[] paragraphInfo = null;
         final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null;
@@ -739,8 +740,8 @@
             constraints.setIndent(firstWidth, firstWidthLineCount);
             constraints.setTabStops(variableTabStops, TAB_INCREMENT);
 
-            NativeLineBreaker.Result res = lineBreaker.computeLineBreaks(
-                    measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount);
+            LineBreaker.Result res = lineBreaker.computeLineBreaks(
+                    measuredPara.getMeasuredText(), constraints, mLineCount);
             int breakCount = res.getLineCount();
             if (lineBreakCapacity < breakCount) {
                 lineBreakCapacity = breakCount;
@@ -776,7 +777,7 @@
                         width += lineWidths[i];
                     } else {
                         for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
-                            width += measuredPara.getCharWidthAt(j - paraStart);
+                            width += measuredPara.getCharWidthAt(j);
                         }
                     }
                     hasTab |= hasTabs[i];
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index e31e928..195de07 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -16,7 +16,10 @@
 
 package android.text;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.annotation.FloatRange;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -42,6 +45,7 @@
 import android.text.style.EasyEditSpan;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
+import android.text.style.LineBackgroundSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
@@ -69,7 +73,9 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
 import java.lang.reflect.Array;
+import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -87,6 +93,44 @@
     private static final String ELLIPSIS_NORMAL = "\u2026"; // HORIZONTAL ELLIPSIS (…)
     private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // TWO DOT LEADER (‥)
 
+    private static final int LINE_FEED_CODE_POINT = 10;
+    private static final int NBSP_CODE_POINT = 160;
+
+    /**
+     * Flags for {@link #makeSafeForPresentation(String, int, float, int)}
+     *
+     * @hide
+     */
+    @Retention(SOURCE)
+    @IntDef(flag = true, prefix = "CLEAN_STRING_FLAG_",
+            value = {SAFE_STRING_FLAG_TRIM, SAFE_STRING_FLAG_SINGLE_LINE,
+                    SAFE_STRING_FLAG_FIRST_LINE})
+    public @interface SafeStringFlags {}
+
+    /**
+     * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges
+     * of the label.
+     *
+     * @see #makeSafeForPresentation(String, int, float, int)
+     */
+    public static final int SAFE_STRING_FLAG_TRIM = 0x1;
+
+    /**
+     * Force entire string into single line of text (no newlines). Cannot be set at the same time as
+     * {@link #SAFE_STRING_FLAG_FIRST_LINE}.
+     *
+     * @see #makeSafeForPresentation(String, int, float, int)
+     */
+    public static final int SAFE_STRING_FLAG_SINGLE_LINE = 0x2;
+
+    /**
+     * Return only first line of text (truncate at first newline). Cannot be set at the same time as
+     * {@link #SAFE_STRING_FLAG_SINGLE_LINE}.
+     *
+     * @see #makeSafeForPresentation(String, int, float, int)
+     */
+    public static final int SAFE_STRING_FLAG_FIRST_LINE = 0x4;
+
     /** {@hide} */
     @NonNull
     public static String getEllipsisString(@NonNull TextUtils.TruncateAt method) {
@@ -687,7 +731,9 @@
     /** @hide */
     public static final int ACCESSIBILITY_URL_SPAN = 26;
     /** @hide */
-    public static final int LAST_SPAN = ACCESSIBILITY_URL_SPAN;
+    public static final int LINE_BACKGROUND_SPAN = 27;
+    /** @hide */
+    public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -878,6 +924,10 @@
                     readSpan(p, sp, new AccessibilityURLSpan(p));
                     break;
 
+                case LINE_BACKGROUND_SPAN:
+                    readSpan(p, sp, new LineBackgroundSpan.Standard(p));
+                    break;
+
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
@@ -2116,6 +2166,222 @@
         return trimmed;
     }
 
+    private static boolean isNewline(int codePoint) {
+        int type = Character.getType(codePoint);
+        return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
+                || codePoint == LINE_FEED_CODE_POINT;
+    }
+
+    private static boolean isWhiteSpace(int codePoint) {
+        return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
+    }
+
+    /**
+     * Remove html, remove bad characters, and truncate string.
+     *
+     * <p>This method is meant to remove common mistakes and nefarious formatting from strings that
+     * were loaded from untrusted sources (such as other packages).
+     *
+     * <p>This method first {@link Html#fromHtml treats the string like HTML} and then ...
+     * <ul>
+     * <li>Removes new lines or truncates at first new line
+     * <li>Trims the white-space off the end
+     * <li>Truncates the string
+     * </ul>
+     * ... if specified.
+     *
+     * @param unclean The input string
+     * @param maxCharactersToConsider The maximum number of characters of {@code unclean} to
+     *                                consider from the input string. {@code 0} disables this
+     *                                feature.
+     * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42.
+     *                     This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br />
+     *                     Usually ellipsizing should be left to the view showing the string. If a
+     *                     string is used as an input to another string, it might be useful to
+     *                     control the length of the input string though. {@code 0} disables this
+     *                     feature.
+     * @param flags Flags controlling cleaning behavior (Can be {@link #SAFE_STRING_FLAG_TRIM},
+     *              {@link #SAFE_STRING_FLAG_SINGLE_LINE},
+     *              and {@link #SAFE_STRING_FLAG_FIRST_LINE})
+     *
+     * @return The cleaned string
+     */
+    public static @NonNull CharSequence makeSafeForPresentation(@NonNull String unclean,
+            @IntRange(from = 0) int maxCharactersToConsider,
+            @FloatRange(from = 0) float ellipsizeDip, @SafeStringFlags int flags) {
+        boolean onlyKeepFirstLine = ((flags & SAFE_STRING_FLAG_FIRST_LINE) != 0);
+        boolean forceSingleLine = ((flags & SAFE_STRING_FLAG_SINGLE_LINE) != 0);
+        boolean trim = ((flags & SAFE_STRING_FLAG_TRIM) != 0);
+
+        Preconditions.checkNotNull(unclean);
+        Preconditions.checkArgumentNonnegative(maxCharactersToConsider);
+        Preconditions.checkArgumentNonNegative(ellipsizeDip, "ellipsizeDip");
+        Preconditions.checkFlagsArgument(flags, SAFE_STRING_FLAG_TRIM
+                | SAFE_STRING_FLAG_SINGLE_LINE | SAFE_STRING_FLAG_FIRST_LINE);
+        Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine),
+                "Cannot set SAFE_STRING_FLAG_SINGLE_LINE and SAFE_STRING_FLAG_FIRST_LINE at the"
+                        + "same time");
+
+        String shortString;
+        if (maxCharactersToConsider > 0) {
+            shortString = unclean.substring(0, Math.min(unclean.length(), maxCharactersToConsider));
+        } else {
+            shortString = unclean;
+        }
+
+        // Treat string as HTML. This
+        // - converts HTML symbols: e.g. &szlig; -> ß
+        // - applies some HTML tags: e.g. <br> -> \n
+        // - removes invalid characters such as \b
+        // - removes html styling, such as <b>
+        // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc
+        // - replaces some html tags by "object replacement" markers: <img> -> \ufffc
+        // - Removes leading white space
+        // - Removes all trailing white space beside a single space
+        // - Collapses double white space
+        StringWithRemovedChars gettingCleaned = new StringWithRemovedChars(
+                Html.fromHtml(shortString).toString());
+
+        int firstNonWhiteSpace = -1;
+        int firstTrailingWhiteSpace = -1;
+
+        // Remove new lines (if requested) and control characters.
+        int uncleanLength = gettingCleaned.length();
+        for (int offset = 0; offset < uncleanLength; ) {
+            int codePoint = gettingCleaned.codePointAt(offset);
+            int type = Character.getType(codePoint);
+            int codePointLen = Character.charCount(codePoint);
+            boolean isNewline = isNewline(codePoint);
+
+            if (onlyKeepFirstLine && isNewline) {
+                gettingCleaned.removeAllCharAfter(offset);
+                break;
+            } else if (forceSingleLine && isNewline) {
+                gettingCleaned.removeRange(offset, offset + codePointLen);
+            } else if (type == Character.CONTROL && !isNewline) {
+                gettingCleaned.removeRange(offset, offset + codePointLen);
+            } else if (trim && !isWhiteSpace(codePoint)) {
+                // This is only executed if the code point is not removed
+                if (firstNonWhiteSpace == -1) {
+                    firstNonWhiteSpace = offset;
+                }
+                firstTrailingWhiteSpace = offset + codePointLen;
+            }
+
+            offset += codePointLen;
+        }
+
+        if (trim) {
+            // Remove leading and trailing white space
+            if (firstNonWhiteSpace == -1) {
+                // No non whitespace found, remove all
+                gettingCleaned.removeAllCharAfter(0);
+            } else {
+                if (firstNonWhiteSpace > 0) {
+                    gettingCleaned.removeAllCharBefore(firstNonWhiteSpace);
+                }
+                if (firstTrailingWhiteSpace < uncleanLength) {
+                    gettingCleaned.removeAllCharAfter(firstTrailingWhiteSpace);
+                }
+            }
+        }
+
+        if (ellipsizeDip == 0) {
+            return gettingCleaned.toString();
+        } else {
+            // Truncate
+            final TextPaint paint = new TextPaint();
+            paint.setTextSize(42);
+
+            return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
+                    TextUtils.TruncateAt.END);
+        }
+    }
+
+    /**
+     * A special string manipulation class. Just records removals and executes the when onString()
+     * is called.
+     */
+    private static class StringWithRemovedChars {
+        /** The original string */
+        private final String mOriginal;
+
+        /**
+         * One bit per char in string. If bit is set, character needs to be removed. If whole
+         * bit field is not initialized nothing needs to be removed.
+         */
+        private BitSet mRemovedChars;
+
+        StringWithRemovedChars(@NonNull String original) {
+            mOriginal = original;
+        }
+
+        /**
+         * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including
+         * firstNonRemoved) as removed.
+         */
+        void removeRange(int firstRemoved, int firstNonRemoved) {
+            if (mRemovedChars == null) {
+                mRemovedChars = new BitSet(mOriginal.length());
+            }
+
+            mRemovedChars.set(firstRemoved, firstNonRemoved);
+        }
+
+        /**
+         * Remove all characters before {@code firstNonRemoved}.
+         */
+        void removeAllCharBefore(int firstNonRemoved) {
+            if (mRemovedChars == null) {
+                mRemovedChars = new BitSet(mOriginal.length());
+            }
+
+            mRemovedChars.set(0, firstNonRemoved);
+        }
+
+        /**
+         * Remove all characters after and including {@code firstRemoved}.
+         */
+        void removeAllCharAfter(int firstRemoved) {
+            if (mRemovedChars == null) {
+                mRemovedChars = new BitSet(mOriginal.length());
+            }
+
+            mRemovedChars.set(firstRemoved, mOriginal.length());
+        }
+
+        @Override
+        public String toString() {
+            // Common case, no chars removed
+            if (mRemovedChars == null) {
+                return mOriginal;
+            }
+
+            StringBuilder sb = new StringBuilder(mOriginal.length());
+            for (int i = 0; i < mOriginal.length(); i++) {
+                if (!mRemovedChars.get(i)) {
+                    sb.append(mOriginal.charAt(i));
+                }
+            }
+
+            return sb.toString();
+        }
+
+        /**
+         * Return length or the original string
+         */
+        int length() {
+            return mOriginal.length();
+        }
+
+        /**
+         * Return codePoint of original string at a certain {@code offset}
+         */
+        int codePointAt(int offset) {
+            return mOriginal.codePointAt(offset);
+        }
+    }
+
     private static Object sLock = new Object();
 
     private static char[] sTemp = null;
diff --git a/core/java/android/text/style/LineBackgroundSpan.java b/core/java/android/text/style/LineBackgroundSpan.java
index 9c7859f..5a55fd7 100644
--- a/core/java/android/text/style/LineBackgroundSpan.java
+++ b/core/java/android/text/style/LineBackgroundSpan.java
@@ -16,15 +16,110 @@
 
 package android.text.style;
 
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
 
-public interface LineBackgroundSpan
-extends ParagraphStyle
+/**
+ * Used to change the background of lines where the span is attached to.
+ */
+public interface LineBackgroundSpan extends ParagraphStyle
 {
-    public void drawBackground(Canvas c, Paint p,
-                               int left, int right,
-                               int top, int baseline, int bottom,
-                               CharSequence text, int start, int end,
-                               int lnum);
+    /**
+     * Draw the background on the canvas.
+     *
+     * @param canvas      canvas on which the span should be rendered
+     * @param paint       paint used to draw text, which should be left unchanged on exit
+     * @param left        left position of the line relative to input canvas, in pixels
+     * @param right       right position of the line relative to input canvas, in pixels
+     * @param top         top position of the line relative to input canvas, in pixels
+     * @param baseline    baseline of the text relative to input canvas, in pixels
+     * @param bottom      bottom position of the line relative to input canvas, in pixels
+     * @param text        current text
+     * @param start       start character index of the line
+     * @param end         end character index of the line
+     * @param lineNumber  line number in the current text layout
+     */
+    void drawBackground(@NonNull Canvas canvas, @NonNull Paint paint,
+                               @Px int left, @Px int right,
+                               @Px int top, @Px int baseline, @Px int bottom,
+                               @NonNull CharSequence text, int start, int end,
+                               int lineNumber);
+    /**
+     * Default implementation of the {@link LineBackgroundSpan}, which changes the background
+     * color of the lines to which the span is attached.
+     */
+    class Standard implements LineBackgroundSpan, ParcelableSpan {
+
+        private final int mColor;
+
+        /**
+         * Constructor taking a color integer.
+         *
+         * @param color Color integer that defines the background color.
+         */
+        public Standard(@ColorInt int color) {
+            mColor = color;
+        }
+
+        /**
+         * Creates a {@link LineBackgroundSpan.Standard} from a parcel
+         */
+        public Standard(@NonNull Parcel src) {
+            mColor = src.readInt();
+        }
+
+        @Override
+        public int getSpanTypeId() {
+            return getSpanTypeIdInternal();
+        }
+
+        /** @hide */
+        @Override
+        public int getSpanTypeIdInternal() {
+            return TextUtils.LINE_BACKGROUND_SPAN;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            writeToParcelInternal(dest, flags);
+        }
+
+        /** @hide */
+        @Override
+        public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mColor);
+        }
+
+        /**
+         * @return the color of this span.
+         * @see Standard#Standard(int)
+         */
+        @ColorInt
+        public final int getColor() {
+            return mColor;
+        }
+
+        @Override
+        public void drawBackground(@NonNull Canvas canvas, @NonNull Paint paint,
+                @Px int left, @Px int right,
+                @Px int top, @Px int baseline, @Px int bottom,
+                @NonNull CharSequence text, int start, int end,
+                int lineNumber) {
+            final int originColor = paint.getColor();
+            paint.setColor(mColor);
+            canvas.drawRect(left, right, top, bottom, paint);
+            paint.setColor(originColor);
+        }
+    }
 }
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 648bd1b..2355769 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -22,7 +22,8 @@
 import android.content.res.TypedArray;
 import android.graphics.LeakyTypefaceStorage;
 import android.graphics.Typeface;
-import android.graphics.fonts.Font;
+import android.graphics.fonts.FontStyle;
+import android.os.LocaleList;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
@@ -66,6 +67,7 @@
     private final Typeface mTypeface;
 
     private final int mTextFontWeight;
+    private final LocaleList mTextLocales;
 
     private final float mShadowRadius;
     private final float mShadowDx;
@@ -149,6 +151,19 @@
         mTextFontWeight = a.getInt(com.android.internal.R.styleable
                 .TextAppearance_textFontWeight, -1);
 
+        final String localeString = a.getString(com.android.internal.R.styleable
+                .TextAppearance_textLocale);
+        if (localeString != null) {
+            LocaleList localeList = LocaleList.forLanguageTags(localeString);
+            if (!localeList.isEmpty()) {
+                mTextLocales = localeList;
+            } else {
+                mTextLocales = null;
+            }
+        } else {
+            mTextLocales = null;
+        }
+
         mShadowRadius = a.getFloat(com.android.internal.R.styleable
                 .TextAppearance_shadowRadius, 0.0f);
         mShadowDx = a.getFloat(com.android.internal.R.styleable
@@ -201,6 +216,7 @@
         mTypeface = null;
 
         mTextFontWeight = -1;
+        mTextLocales = null;
 
         mShadowRadius = 0.0f;
         mShadowDx = 0.0f;
@@ -233,6 +249,7 @@
         mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
 
         mTextFontWeight = src.readInt();
+        mTextLocales = src.readParcelable(LocaleList.class.getClassLoader());
 
         mShadowRadius = src.readFloat();
         mShadowDx = src.readFloat();
@@ -285,6 +302,7 @@
         LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
 
         dest.writeInt(mTextFontWeight);
+        dest.writeParcelable(mTextLocales, flags);
 
         dest.writeFloat(mShadowRadius);
         dest.writeFloat(mShadowDx);
@@ -349,6 +367,15 @@
     }
 
     /**
+     * Returns the {@link android.os.LocaleList} specified by this span, or <code>null</code>
+     * if it does not specify one.
+     */
+    @Nullable
+    public LocaleList getTextLocales() {
+        return mTextLocales;
+    }
+
+    /**
      * Returns the typeface specified by this span, or <code>null</code>
      * if it does not specify one.
      */
@@ -463,7 +490,7 @@
         if (styledTypeface != null) {
             final Typeface readyTypeface;
             if (mTextFontWeight >= 0) {
-                final int weight = Math.min(Font.FONT_WEIGHT_MAX, mTextFontWeight);
+                final int weight = Math.min(FontStyle.FONT_WEIGHT_MAX, mTextFontWeight);
                 final boolean italic = (style & Typeface.ITALIC) != 0;
                 readyTypeface = ds.setTypeface(Typeface.create(styledTypeface, weight, italic));
             } else {
@@ -487,6 +514,10 @@
             ds.setTextSize(mTextSize);
         }
 
+        if (mTextLocales != null) {
+            ds.setTextLocales(mTextLocales);
+        }
+
         if (mHasElegantTextHeight) {
             ds.setElegantTextHeight(mElegantTextHeight);
         }
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 4608e20..0e5252e 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -315,6 +315,7 @@
                     ArrayList<Transition> currentTransitions =
                             runningTransitions.get(mSceneRoot);
                     currentTransitions.remove(transition);
+                    transition.removeListener(this);
                 }
             });
             mTransition.captureValues(mSceneRoot, false);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 183e833..db2c190 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -47,7 +47,7 @@
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
         DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
         DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
-        DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
+        DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put(EMERGENCY_DIAL_SHORTCUTS, "false");
     }
 
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index 209451b..cd2b6ce 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -737,8 +737,7 @@
         fillBuffer();
         if (mOffset + n <= mEnd) {
             // fast path read. String is well within the current buffer
-            String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n,
-                    StandardCharsets.UTF_8);
+            String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8);
             incOffset(n);
             return value;
         } else if (n <= mBufferSize) {
@@ -752,14 +751,13 @@
             mDiscardedBytes += mOffset;
             mOffset = 0;
 
-            String value = StringFactory.newStringFromBytes(mBuffer, mOffset, n,
-                    StandardCharsets.UTF_8);
+            String value = new String(mBuffer, mOffset, n, StandardCharsets.UTF_8);
             incOffset(n);
             return value;
         }
         // Otherwise, the string is too large to use the buffer. Create the string from a
         // separate byte array.
-        return StringFactory.newStringFromBytes(readRawBytes(n), 0, n, StandardCharsets.UTF_8);
+        return new String(readRawBytes(n), 0, n, StandardCharsets.UTF_8);
     }
 
     /**
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index fa7b067..98ed217 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -18,6 +18,8 @@
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 import android.widget.FrameLayout;
 
 import java.util.ArrayList;
@@ -46,8 +48,8 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        if (canvas instanceof DisplayListCanvas) {
-            DisplayListCanvas dlCanvas = (DisplayListCanvas) canvas;
+        if (canvas instanceof RecordingCanvas) {
+            RecordingCanvas dlCanvas = (RecordingCanvas) canvas;
             mView.mRecreateDisplayList = true;
             RenderNode renderNode = mView.updateDisplayListIfDirty();
             if (renderNode.isValid()) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 3e559d9..0c3a295 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -226,27 +226,52 @@
     int getPreferredOptionsPanelGravity(int displayId);
 
     /**
-     * Lock the device orientation to the specified rotation, or to the
-     * current rotation if -1.  Sensor input will be ignored until
-     * thawRotation() is called.
-     * @hide
+     * Equivalent to calling {@link #freezeDisplayRotation(int, int)} with {@link
+     * android.view.Display#DEFAULT_DISPLAY} and given rotation.
      */
     void freezeRotation(int rotation);
 
     /**
-     * Release the orientation lock imposed by freezeRotation().
-     * @hide
+     * Equivalent to calling {@link #thawDisplayRotation(int)} with {@link
+     * android.view.Display#DEFAULT_DISPLAY}.
      */
     void thawRotation();
 
     /**
-     * Gets whether the rotation is frozen.
-     *
-     * @return Whether the rotation is frozen.
+     * Equivelant to call {@link #isDisplayRotationFrozen(int)} with {@link
+     * android.view.Display#DEFAULT_DISPLAY}.
      */
     boolean isRotationFrozen();
 
     /**
+     * Lock the display orientation to the specified rotation, or to the current
+     * rotation if -1. Sensor input will be ignored until thawRotation() is called.
+     *
+     * @param displayId the ID of display which rotation should be frozen.
+     * @param rotation one of {@link android.view.Surface#ROTATION_0},
+     *        {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180},
+     *        {@link android.view.Surface#ROTATION_270} or -1 to freeze it to current rotation.
+     * @hide
+     */
+    void freezeDisplayRotation(int displayId, int rotation);
+
+    /**
+     * Release the orientation lock imposed by freezeRotation() on the display.
+     *
+     * @param displayId the ID of display which rotation should be thawed.
+     * @hide
+     */
+    void thawDisplayRotation(int displayId);
+
+    /**
+     * Gets whether the rotation is frozen on the display.
+     *
+     * @param displayId the ID of display which frozen is needed.
+     * @return Whether the rotation is frozen.
+     */
+    boolean isDisplayRotationFrozen(int displayId);
+
+    /**
      * Screenshot the current wallpaper layer, including the whole screen.
      */
     Bitmap screenshotWallpaper();
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 29c58dc..b59d8c7 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1776,6 +1776,47 @@
     static public MotionEvent obtain(long downTime, long eventTime, int action,
             float x, float y, float pressure, float size, int metaState,
             float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
+        return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
+                xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+                DEFAULT_DISPLAY);
+    }
+
+    /**
+     * Create a new MotionEvent, filling in all of the basic values that
+     * define the motion.
+     *
+     * @param downTime The time (in ms) when the user originally pressed down to start
+     * a stream of position events.  This must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param eventTime  The the time (in ms) when this specific event was generated.  This
+     * must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param action The kind of action being performed, such as {@link #ACTION_DOWN}.
+     * @param x The X coordinate of this event.
+     * @param y The Y coordinate of this event.
+     * @param pressure The current pressure of this event.  The pressure generally
+     * ranges from 0 (no pressure at all) to 1 (normal pressure), however
+     * values higher than 1 may be generated depending on the calibration of
+     * the input device.
+     * @param size A scaled value of the approximate size of the area being pressed when
+     * touched with the finger. The actual value in pixels corresponding to the finger
+     * touch is normalized with a device specific range of values
+     * and scaled to a value between 0 and 1.
+     * @param metaState The state of any meta / modifier keys that were in effect when
+     * the event was generated.
+     * @param xPrecision The precision of the X coordinate being reported.
+     * @param yPrecision The precision of the Y coordinate being reported.
+     * @param deviceId The id for the device that this event came from.  An id of
+     * zero indicates that the event didn't come from a physical device; other
+     * numbers are arbitrary and you shouldn't depend on the values.
+     * @param source The source of this event.
+     * @param edgeFlags A bitfield indicating which edges, if any, were touched by this
+     * MotionEvent.
+     * @param displayId The display ID associated with this event.
+     * @hide
+     */
+    public static MotionEvent obtain(long downTime, long eventTime, int action,
+            float x, float y, float pressure, float size, int metaState,
+            float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source,
+            int displayId) {
         MotionEvent ev = obtain();
         synchronized (gSharedTempLock) {
             ensureSharedTempPointerCapacity(1);
@@ -1791,7 +1832,7 @@
             pc[0].size = size;
 
             ev.mNativePtr = nativeInitialize(ev.mNativePtr,
-                    deviceId, InputDevice.SOURCE_UNKNOWN, DEFAULT_DISPLAY,
+                    deviceId, source, displayId,
                     action, 0, edgeFlags, metaState, 0,
                     0, 0, xPrecision, yPrecision,
                     downTime * NS_PER_MS, eventTime * NS_PER_MS,
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index e48bcfd..9d31bd1 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -22,6 +22,8 @@
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 import android.util.SparseIntArray;
 
 import com.android.internal.util.VirtualRefBasePtr;
@@ -286,8 +288,8 @@
         setTarget(mViewTarget.mRenderNode);
     }
 
-    /** Sets the animation target to the owning view of the DisplayListCanvas */
-    public void setTarget(DisplayListCanvas canvas) {
+    /** Sets the animation target to the owning view of the RecordingCanvas */
+    public void setTarget(RecordingCanvas canvas) {
         setTarget(canvas.mNode);
     }
 
@@ -405,7 +407,7 @@
         return listeners;
     }
 
-    long getNativeAnimator() {
+    public long getNativeAnimator() {
         return mNativePtr.get();
     }
 
diff --git a/core/java/android/view/RenderNodeAnimatorSetHelper.java b/core/java/android/view/RenderNodeAnimatorSetHelper.java
index e1ef059..d222e07 100644
--- a/core/java/android/view/RenderNodeAnimatorSetHelper.java
+++ b/core/java/android/view/RenderNodeAnimatorSetHelper.java
@@ -16,6 +16,8 @@
 package android.view;
 
 import android.animation.TimeInterpolator;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
 
 import com.android.internal.view.animation.FallbackLUTInterpolator;
 import com.android.internal.view.animation.NativeInterpolatorFactory;
@@ -29,10 +31,12 @@
  */
 public class RenderNodeAnimatorSetHelper {
 
-    public static RenderNode getTarget(DisplayListCanvas recordingCanvas) {
+    /** checkstyle @hide */
+    public static RenderNode getTarget(RecordingCanvas recordingCanvas) {
         return recordingCanvas.mNode;
     }
 
+    /** checkstyle @hide */
     public static long createNativeInterpolator(TimeInterpolator interpolator, long
             duration) {
         if (interpolator == null) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 6fb1bba..f3cb376 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -22,7 +22,9 @@
 import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.graphics.SurfaceTexture;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -889,13 +891,13 @@
     private final class HwuiContext {
         private final RenderNode mRenderNode;
         private long mHwuiRenderer;
-        private DisplayListCanvas mCanvas;
+        private RecordingCanvas mCanvas;
         private final boolean mIsWideColorGamut;
 
         HwuiContext(boolean isWideColorGamut) {
             mRenderNode = RenderNode.create("HwuiCanvas", null);
             mRenderNode.setClipToBounds(false);
-            mRenderNode.setAllowForceDark(false);
+            mRenderNode.setForceDarkAllowed(false);
             mIsWideColorGamut = isWideColorGamut;
             mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject,
                     isWideColorGamut);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index e71182c..67f9399 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -30,6 +30,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.RenderNode;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
diff --git a/core/java/android/view/TextureLayer.java b/core/java/android/view/TextureLayer.java
index 35a886f..d89d634 100644
--- a/core/java/android/view/TextureLayer.java
+++ b/core/java/android/view/TextureLayer.java
@@ -31,7 +31,7 @@
  *
  * @hide
  */
-final class TextureLayer {
+public final class TextureLayer {
     private ThreadedRenderer mRenderer;
     private VirtualRefBasePtr mFinalizer;
 
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 997e48fe..0175ba2 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.graphics.drawable.Drawable;
@@ -343,7 +344,7 @@
         properties (alpha, layer paint) affect all of the content of a TextureView. */
 
         if (canvas.isHardwareAccelerated()) {
-            DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+            RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
 
             TextureLayer layer = getTextureLayer();
             if (layer != null) {
@@ -351,7 +352,7 @@
                 applyTransformMatrix();
 
                 mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
-                displayListCanvas.drawTextureLayer(layer);
+                recordingCanvas.drawTextureLayer(layer);
             }
         }
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 42690ce..c1ab4d4 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -24,7 +24,9 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -693,7 +695,7 @@
         updateViewTreeDisplayList(view);
 
         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
-            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
+            RecordingCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
             try {
                 final int saveCount = canvas.save();
                 canvas.translate(mInsetLeft, mInsetTop);
@@ -770,7 +772,7 @@
          *
          * @param canvas The Canvas used to render the view.
          */
-        void onPreDraw(DisplayListCanvas canvas);
+        void onPreDraw(RecordingCanvas canvas);
 
         /**
          * Invoked after a view is drawn by a threaded renderer.
@@ -778,7 +780,7 @@
          *
          * @param canvas The Canvas used to render the view.
          */
-        void onPostDraw(DisplayListCanvas canvas);
+        void onPostDraw(RecordingCanvas canvas);
     }
 
     /**
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
index 06b73dd..6fb32e3 100644
--- a/core/java/android/view/TouchDelegate.java
+++ b/core/java/android/view/TouchDelegate.java
@@ -165,7 +165,11 @@
     public TouchDelegateInfo getTouchDelegateInfo() {
         if (mTouchDelegateInfo == null) {
             final ArrayMap<Region, View> targetMap = new ArrayMap<>(1);
-            targetMap.put(new Region(mBounds), mDelegateView);
+            Rect bounds = mBounds;
+            if (bounds == null) {
+                bounds = new Rect();
+            }
+            targetMap.put(new Region(bounds), mDelegateView);
             mTouchDelegateInfo = new TouchDelegateInfo(targetMap);
         }
         return mTouchDelegateInfo;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 29d3742..1157b28 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -57,9 +57,11 @@
 import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.graphics.RenderNode;
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -5540,6 +5542,10 @@
                     break;
                 case com.android.internal.R.styleable.View_accessibilityHeading:
                     setAccessibilityHeading(a.getBoolean(attr, false));
+                    break;
+                case R.styleable.View_forceDarkAllowed:
+                    mRenderNode.setForceDarkAllowed(a.getBoolean(attr, true));
+                    break;
             }
         }
 
@@ -15284,11 +15290,9 @@
      * If a theme is isLightTheme="false", then force dark is globally disabled for that theme.
      *
      * @param allow Whether or not to allow force dark.
-     *
-     * @hide
      */
-    public void setAllowForceDark(boolean allow) {
-        if (mRenderNode.setAllowForceDark(allow)) {
+    public void setForceDarkAllowed(boolean allow) {
+        if (mRenderNode.setForceDarkAllowed(allow)) {
             // Currently toggling force-dark requires a new display list push to apply
             // TODO: Make it not clobber the display list so this is just a damageSelf() instead
             invalidate();
@@ -15296,15 +15300,13 @@
     }
 
     /**
-     * See {@link #setAllowForceDark(boolean)}
+     * See {@link #setForceDarkAllowed(boolean)}
      *
      * @return true if force dark is allowed (default), false if it is disabled
-     *
-     * @hide
      */
     @ViewDebug.ExportedProperty(category = "drawing")
-    public boolean getAllowForceDark() {
-        return mRenderNode.getAllowForceDark();
+    public boolean isForceDarkAllowed() {
+        return mRenderNode.isForceDarkAllowed();
     }
 
     /**
@@ -19233,7 +19235,7 @@
             int height = mBottom - mTop;
             int layerType = getLayerType();
 
-            final DisplayListCanvas canvas = renderNode.start(width, height);
+            final RecordingCanvas canvas = renderNode.start(width, height);
 
             try {
                 if (layerType == LAYER_TYPE_SOFTWARE) {
@@ -20250,7 +20252,7 @@
         if (!drawingWithDrawingCache) {
             if (drawingWithRenderNode) {
                 mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
+                ((RecordingCanvas) canvas).drawRenderNode(renderNode);
             } else {
                 // Fast path for layouts with no backgrounds
                 if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
@@ -20581,7 +20583,7 @@
             final RenderNode renderNode = mBackgroundRenderNode;
             if (renderNode != null && renderNode.isValid()) {
                 setBackgroundRenderNodeProperties(renderNode);
-                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
+                ((RecordingCanvas) canvas).drawRenderNode(renderNode);
                 return;
             }
         }
@@ -20633,7 +20635,7 @@
         final Rect bounds = drawable.getBounds();
         final int width = bounds.width();
         final int height = bounds.height();
-        final DisplayListCanvas canvas = renderNode.start(width, height);
+        final RecordingCanvas canvas = renderNode.start(width, height);
 
         // Reverse left/top translation done by drawable canvas, which will
         // instead be applied by rendernode's LTRB bounds below. This way, the
@@ -24192,7 +24194,7 @@
      *     </ul>
      * @return {@code true} if the method completes successfully, or
      * {@code false} if it fails anywhere. Returning {@code false} means the system was unable to
-     * do a drag, and so no drag operation is in progress.
+     * do a drag because of another ongoing operation or some other reasons.
      */
     public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,
             Object myLocalState, int flags) {
@@ -24235,51 +24237,51 @@
             Log.d(VIEW_LOG_TAG, "drag shadow: width=" + shadowSize.x + " height=" + shadowSize.y
                     + " shadowX=" + shadowTouchPoint.x + " shadowY=" + shadowTouchPoint.y);
         }
-        if (mAttachInfo.mDragSurface != null) {
-            mAttachInfo.mDragSurface.release();
-        }
-        mAttachInfo.mDragSurface = new Surface();
-        mAttachInfo.mDragToken = null;
 
         final ViewRootImpl root = mAttachInfo.mViewRootImpl;
         final SurfaceSession session = new SurfaceSession(root.mSurface);
-        final SurfaceControl surface = new SurfaceControl.Builder(session)
+        final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
                 .setSize(shadowSize.x, shadowSize.y)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .build();
+        final Surface surface = new Surface();
+        surface.copyFrom(surfaceControl);
+        IBinder token = null;
         try {
-            mAttachInfo.mDragSurface.copyFrom(surface);
-            final Canvas canvas = mAttachInfo.mDragSurface.lockCanvas(null);
+            final Canvas canvas = surface.lockCanvas(null);
             try {
                 canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                 shadowBuilder.onDrawShadow(canvas);
             } finally {
-                mAttachInfo.mDragSurface.unlockCanvasAndPost(canvas);
+                surface.unlockCanvasAndPost(canvas);
             }
 
-            // Cache the local state object for delivery with DragEvents
-            root.setLocalDragState(myLocalState);
-
             // repurpose 'shadowSize' for the last touch point
             root.getLastTouchPoint(shadowSize);
 
-            mAttachInfo.mDragToken = mAttachInfo.mSession.performDrag(
-                    mAttachInfo.mWindow, flags, surface, root.getLastTouchSource(),
+            token = mAttachInfo.mSession.performDrag(
+                    mAttachInfo.mWindow, flags, surfaceControl, root.getLastTouchSource(),
                     shadowSize.x, shadowSize.y, shadowTouchPoint.x, shadowTouchPoint.y, data);
             if (ViewDebug.DEBUG_DRAG) {
-                Log.d(VIEW_LOG_TAG, "performDrag returned " + mAttachInfo.mDragToken);
+                Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
             }
-
-            return mAttachInfo.mDragToken != null;
+            if (token != null) {
+                if (mAttachInfo.mDragSurface != null) {
+                    mAttachInfo.mDragSurface.release();
+                }
+                mAttachInfo.mDragSurface = surface;
+                mAttachInfo.mDragToken = token;
+                // Cache the local state object for delivery with DragEvents
+                root.setLocalDragState(myLocalState);
+            }
+            return token != null;
         } catch (Exception e) {
             Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
             return false;
         } finally {
-            if (mAttachInfo.mDragToken == null) {
-                mAttachInfo.mDragSurface.destroy();
-                mAttachInfo.mDragSurface = null;
-                root.setLocalDragState(null);
+            if (token == null) {
+                surface.destroy();
             }
             session.kill();
         }
diff --git a/core/java/android/view/ViewAnimationHostBridge.java b/core/java/android/view/ViewAnimationHostBridge.java
index 58f555d..e0fae21 100644
--- a/core/java/android/view/ViewAnimationHostBridge.java
+++ b/core/java/android/view/ViewAnimationHostBridge.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.graphics.RenderNode;
+
 /**
  * Maps a View to a RenderNode's AnimationHost
  *
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8dd0347..292e933 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -23,7 +23,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Picture;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -601,7 +603,7 @@
         }
 
         if (view.isHardwareAccelerated()) {
-            DisplayListCanvas canvas = node.start(dm.widthPixels, dm.heightPixels);
+            RecordingCanvas canvas = node.start(dm.widthPixels, dm.heightPixels);
             try {
                 return profileViewOperation(view, () -> view.draw(canvas));
             } finally {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b3e62d..58febb05 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -541,11 +541,11 @@
     private static final int CHILD_TOP_INDEX = 1;
 
     // Child views of this ViewGroup
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private View[] mChildren;
     // Number of valid children in the mChildren array, the rest should be null or not
     // considered as children
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private int mChildrenCount;
 
     // Whether layout calls are currently being suppressed, controlled by calls to
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index e3e2069..a0ab362 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.graphics.RenderNode;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bef8e8f..170c783 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -51,8 +51,10 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.PorterDuff;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
@@ -1094,15 +1096,21 @@
     private void updateForceDarkMode() {
         if (mAttachInfo.mThreadedRenderer == null) return;
 
-        boolean nightMode = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
-        TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
-        boolean isLightTheme = a.getBoolean(R.styleable.Theme_isLightTheme, false);
-        a.recycle();
+        boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
 
-        boolean changed = mAttachInfo.mThreadedRenderer.setForceDark(nightMode);
-        changed |= mAttachInfo.mThreadedRenderer.getRootNode().setAllowForceDark(isLightTheme);
+        // Allow debug.hwui.force_dark to override the target SDK check
+        if (useAutoDark && !SystemProperties.getBoolean("debug.hwui.force_dark", false)) {
+            useAutoDark = mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q;
+        }
 
-        if (changed) {
+        if (useAutoDark) {
+            TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme);
+            useAutoDark = a.getBoolean(R.styleable.Theme_isLightTheme, true)
+                    && a.getBoolean(R.styleable.Theme_forceDarkAllowed, true);
+            a.recycle();
+        }
+
+        if (mAttachInfo.mThreadedRenderer.setForceDark(useAutoDark)) {
             // TODO: Don't require regenerating all display lists to apply this setting
             invalidateWorld(mView);
         }
@@ -3120,7 +3128,7 @@
     int mHardwareYOffset;
 
     @Override
-    public void onPreDraw(DisplayListCanvas canvas) {
+    public void onPreDraw(RecordingCanvas canvas) {
         // If mCurScrollY is not 0 then this influences the hardwareYOffset. The end result is we
         // can apply offsets that are not handled by anything else, resulting in underdraw as
         // the View is shifted (thus shifting the window background) exposing unpainted
@@ -3134,7 +3142,7 @@
     }
 
     @Override
-    public void onPostDraw(DisplayListCanvas canvas) {
+    public void onPostDraw(RecordingCanvas canvas) {
         drawAccessibilityFocusedDrawableIfNeeded(canvas);
         if (mUseMTRenderer) {
             for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
index b2dc1e9..a997302 100644
--- a/core/java/android/view/WindowCallbacks.java
+++ b/core/java/android/view/WindowCallbacks.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 
 /**
@@ -82,5 +83,5 @@
      *
      * @param canvas The canvas to draw on.
      */
-    void onPostDraw(DisplayListCanvas canvas);
+    void onPostDraw(RecordingCanvas canvas);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index ca2ccaf..e9acdc3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -48,6 +48,7 @@
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventSender;
@@ -58,7 +59,10 @@
 import android.view.autofill.AutofillManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInputConnectionWrapper;
 import com.android.internal.view.IInputContext;
@@ -66,7 +70,6 @@
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
 import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -265,7 +268,7 @@
      * @hide
      */
     public static void ensureDefaultInstanceForDefaultDisplayIfNecessary() {
-        getInstanceInternal();
+        forContextInternal(Display.DEFAULT_DISPLAY, Looper.getMainLooper());
     }
 
     private static final Object sLock = new Object();
@@ -279,6 +282,17 @@
     static InputMethodManager sInstance;
 
     /**
+     * Global map between display to {@link InputMethodManager}.
+     *
+     * <p>Currently this map works like a so-called leaky singleton.  Once an instance is registered
+     * for the associated display ID, that instance will never be garbage collected.</p>
+     *
+     * <p>TODO(Bug 116699479): Implement instance clean up mechanism.</p>
+     */
+    @GuardedBy("sLock")
+    private static final SparseArray<InputMethodManager> sInstanceMap = new SparseArray<>();
+
+    /**
      * @hide Flag for IInputMethodManager.windowGainedFocus: a view in
      * the window has input focus.
      */
@@ -335,6 +349,8 @@
     // Our generic input connection if the current target does not have its own.
     final IInputContext mIInputContext;
 
+    private final int mDisplayId;
+
     /**
      * True if this input method client is active, initially false.
      */
@@ -452,6 +468,29 @@
         return afm != null && afm.isAutofillUiShowing();
     }
 
+    /**
+     * Checks the consistency between {@link InputMethodManager} state and {@link View} state.
+     *
+     * @param view {@link View} to be checked
+     * @return {@code true} if {@code view} is not {@code null} and there is a {@link Context}
+     *         mismatch between {@link InputMethodManager} and {@code view}
+     */
+    private boolean shouldDispatchToViewContext(@Nullable View view) {
+        if (view == null) {
+            return false;
+        }
+        final int viewDisplayId = view.getContext().getDisplayId();
+        if (viewDisplayId != mDisplayId) {
+            Log.w(TAG, "b/117267690: Context mismatch found. view=" + view + " belongs to"
+                    + " displayId=" + viewDisplayId
+                    + " but InputMethodManager belongs to displayId=" + mDisplayId
+                    + ". Use the right InputMethodManager instance to avoid performance overhead.",
+                    new Throwable());
+            return true;
+        }
+        return false;
+    }
+
     private static boolean canStartInput(View servedView) {
         // We can start input ether the servedView has window focus
         // or the activity is showing autofill ui.
@@ -503,17 +542,16 @@
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                     }
-                    startInputInner(InputMethodClient.START_INPUT_REASON_BOUND_TO_IMMS,
-                            null, 0, 0, 0);
+                    startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
                     return;
                 }
                 case MSG_UNBIND: {
                     final int sequence = msg.arg1;
-                    @InputMethodClient.UnbindReason
+                    @UnbindReason
                     final int reason = msg.arg2;
                     if (DEBUG) {
                         Log.i(TAG, "handleMessage: MSG_UNBIND " + sequence +
-                                " reason=" + InputMethodClient.getUnbindReason(reason));
+                                " reason=" + InputMethodDebug.unbindReasonToString(reason));
                     }
                     final boolean startInput;
                     synchronized (mH) {
@@ -530,8 +568,7 @@
                     }
                     if (startInput) {
                         startInputInner(
-                                InputMethodClient.START_INPUT_REASON_UNBOUND_FROM_IMMS, null, 0, 0,
-                                0);
+                                StartInputReason.UNBOUND_FROM_IMMS, null, 0, 0, 0);
                     }
                     return;
                 }
@@ -560,9 +597,8 @@
                         // handling this message.
                         if (mServedView != null && canStartInput(mServedView)) {
                             if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) {
-                                final int reason = active ?
-                                        InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
-                                        InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
+                                final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+                                        : StartInputReason.DEACTIVATED_BY_IMMS;
                                 startInputInner(reason, null, 0, 0, 0);
                             }
                         }
@@ -659,7 +695,7 @@
         }
 
         @Override
-        public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
+        public void onUnbindMethod(int sequence, @UnbindReason int unbindReason) {
             mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
         }
 
@@ -701,65 +737,101 @@
         return false;
     }
 
-    private static IInputMethodManager getIInputMethodManager() throws ServiceNotFoundException {
-        if (!isInEditMode()) {
-            return IInputMethodManager.Stub.asInterface(
-                    ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
-        }
-        // If InputMethodManager is running for layoutlib, stub out IPCs into IMMS.
-        final Class<IInputMethodManager> c = IInputMethodManager.class;
-        return (IInputMethodManager) Proxy.newProxyInstance(c.getClassLoader(),
-                new Class[]{c}, (proxy, method, args) -> {
-                    final Class<?> returnType = method.getReturnType();
-                    if (returnType == boolean.class) {
-                        return false;
-                    } else if (returnType == int.class) {
-                        return 0;
-                    } else if (returnType == long.class) {
-                        return 0L;
-                    } else if (returnType == short.class) {
-                        return 0;
-                    } else if (returnType == char.class) {
-                        return 0;
-                    } else if (returnType == byte.class) {
-                        return 0;
-                    } else if (returnType == float.class) {
-                        return 0f;
-                    } else if (returnType == double.class) {
-                        return 0.0;
-                    } else {
-                        return null;
-                    }
-                });
+    @NonNull
+    private static InputMethodManager createInstance(int displayId, Looper looper) {
+        return isInEditMode() ? createStubInstance(displayId, looper)
+                : createRealInstance(displayId, looper);
     }
 
-    InputMethodManager(Looper looper) throws ServiceNotFoundException {
-        mService = getIInputMethodManager();
+    @NonNull
+    private static InputMethodManager createRealInstance(int displayId, Looper looper) {
+        final IInputMethodManager service;
+        try {
+            service = IInputMethodManager.Stub.asInterface(
+                    ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+        } catch (ServiceNotFoundException e) {
+            throw new IllegalStateException(e);
+        }
+        final InputMethodManager imm = new InputMethodManager(service, displayId, looper);
+        try {
+            service.addClient(imm.mClient, imm.mIInputContext, displayId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return imm;
+    }
+
+    @NonNull
+    private static InputMethodManager createStubInstance(int displayId, Looper looper) {
+        // If InputMethodManager is running for layoutlib, stub out IPCs into IMMS.
+        final Class<IInputMethodManager> c = IInputMethodManager.class;
+        final IInputMethodManager stubInterface =
+                (IInputMethodManager) Proxy.newProxyInstance(c.getClassLoader(),
+                        new Class[]{c}, (proxy, method, args) -> {
+                            final Class<?> returnType = method.getReturnType();
+                            if (returnType == boolean.class) {
+                                return false;
+                            } else if (returnType == int.class) {
+                                return 0;
+                            } else if (returnType == long.class) {
+                                return 0L;
+                            } else if (returnType == short.class) {
+                                return 0;
+                            } else if (returnType == char.class) {
+                                return 0;
+                            } else if (returnType == byte.class) {
+                                return 0;
+                            } else if (returnType == float.class) {
+                                return 0f;
+                            } else if (returnType == double.class) {
+                                return 0.0;
+                            } else {
+                                return null;
+                            }
+                        });
+        return new InputMethodManager(stubInterface, displayId, looper);
+    }
+
+    private InputMethodManager(IInputMethodManager service, int displayId, Looper looper) {
+        mService = service;
         mMainLooper = looper;
         mH = new H(looper);
-        mIInputContext = new ControlledInputConnectionWrapper(looper,
-                mDummyInputConnection, this);
+        mDisplayId = displayId;
+        mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this);
     }
 
     /**
-     * Retrieve the global {@link InputMethodManager} instance, creating it if it doesn't already
-     * exist.
+     * Retrieve an instance for the given {@link Context}, creating it if it doesn't already exist.
      *
-     * @return global {@link InputMethodManager} instance
+     * @param context {@link Context} for which IME APIs need to work
+     * @return {@link InputMethodManager} instance
      * @hide
      */
-    public static InputMethodManager getInstanceInternal() {
+    @NonNull
+    public static InputMethodManager forContext(Context context) {
+        final int displayId = context.getDisplayId();
+        // For better backward compatibility, we always use Looper.getMainLooper() for the default
+        // display case.
+        final Looper looper = displayId == Display.DEFAULT_DISPLAY
+                ? Looper.getMainLooper() : context.getMainLooper();
+        return forContextInternal(displayId, looper);
+    }
+
+    @NonNull
+    private static InputMethodManager forContextInternal(int displayId, Looper looper) {
+        final boolean isDefaultDisplay = displayId == Display.DEFAULT_DISPLAY;
         synchronized (sLock) {
-            if (sInstance == null) {
-                try {
-                    final InputMethodManager imm = new InputMethodManager(Looper.getMainLooper());
-                    imm.mService.addClient(imm.mClient, imm.mIInputContext);
-                    sInstance = imm;
-                } catch (ServiceNotFoundException | RemoteException e) {
-                    throw new IllegalStateException(e);
-                }
+            InputMethodManager instance = sInstanceMap.get(displayId);
+            if (instance != null) {
+                return instance;
             }
-            return sInstance;
+            instance = createInstance(displayId, looper);
+            // For backward compatibility, store the instance also to sInstance for default display.
+            if (sInstance == null && isDefaultDisplay) {
+                sInstance = instance;
+            }
+            sInstanceMap.put(displayId, instance);
+            return instance;
         }
     }
 
@@ -916,6 +988,11 @@
      * input method.
      */
     public boolean isActive(View view) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            return view.getContext().getSystemService(InputMethodManager.class).isActive(view);
+        }
+
         checkFocus();
         synchronized (mH) {
             return (mServedView == view
@@ -991,13 +1068,6 @@
         mNextServedView = null;
         if (mServedView != null) {
             if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
-            if (mCurrentTextBoxAttribute != null) {
-                try {
-                    mService.finishInput(mClient);
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
-            }
             mServedView = null;
             mCompletions = null;
             mServedConnecting = false;
@@ -1006,6 +1076,13 @@
     }
 
     public void displayCompletions(View view, CompletionInfo[] completions) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class)
+                    .displayCompletions(view, completions);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if (mServedView != view && (mServedView == null
@@ -1024,6 +1101,13 @@
     }
 
     public void updateExtractedText(View view, int token, ExtractedText text) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class)
+                    .updateExtractedText(view, token, text);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if (mServedView != view && (mServedView == null
@@ -1065,6 +1149,12 @@
      * 0 or have the {@link #SHOW_IMPLICIT} bit set.
      */
     public boolean showSoftInput(View view, int flags) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            return view.getContext().getSystemService(InputMethodManager.class)
+                    .showSoftInput(view, flags);
+        }
+
         return showSoftInput(view, flags, null);
     }
 
@@ -1127,6 +1217,12 @@
      * {@link #RESULT_HIDDEN}.
      */
     public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            return view.getContext().getSystemService(InputMethodManager.class)
+                    .showSoftInput(view, flags, resultReceiver);
+        }
+
         checkFocus();
         synchronized (mH) {
             if (mServedView != view && (mServedView == null
@@ -1290,6 +1386,12 @@
      * @param view The view whose text has changed.
      */
     public void restartInput(View view) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class).restartInput(view);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if (mServedView != view && (mServedView == null
@@ -1300,11 +1402,10 @@
             mServedConnecting = true;
         }
 
-        startInputInner(InputMethodClient.START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, null, 0,
-                0, 0);
+        startInputInner(StartInputReason.APP_CALLED_RESTART_INPUT_API, null, 0, 0, 0);
     }
 
-    boolean startInputInner(@InputMethodClient.StartInputReason final int startInputReason,
+    boolean startInputInner(@StartInputReason int startInputReason,
             @Nullable IBinder windowGainingFocus, int controlFlags, int softInputMode,
             int windowFlags) {
         final View view;
@@ -1314,7 +1415,7 @@
             // Make sure we have a window token for the served view.
             if (DEBUG) {
                 Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
-                        " reason=" + InputMethodClient.getStartInputReason(startInputReason));
+                        " reason=" + InputMethodDebug.startInputReasonToString(startInputReason));
             }
             if (view == null) {
                 if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
@@ -1433,7 +1534,7 @@
                 if (res == null) {
                     Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
                             + " null. startInputReason="
-                            + InputMethodClient.getStartInputReason(startInputReason)
+                            + InputMethodDebug.startInputReasonToString(startInputReason)
                             + " editorInfo=" + tba
                             + " controlFlags=#" + Integer.toHexString(controlFlags));
                     return false;
@@ -1568,7 +1669,7 @@
     @UnsupportedAppUsage
     public void checkFocus() {
         if (checkFocusNoStartInput(false)) {
-            startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
+            startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
         }
     }
 
@@ -1631,7 +1732,7 @@
         boolean forceNewFocus = false;
         synchronized (mH) {
             if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
-                    + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+                    + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                     + " first=" + first + " flags=#"
                     + Integer.toHexString(windowFlags));
             if (mRestartOnNextWindowFocus) {
@@ -1658,8 +1759,8 @@
             // should be done in conjunction with telling the system service
             // about the window gaining focus, to help make the transition
             // smooth.
-            if (startInputInner(InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN,
-                    rootView.getWindowToken(), controlFlags, softInputMode, windowFlags)) {
+            if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(),
+                    controlFlags, softInputMode, windowFlags)) {
                 return;
             }
         }
@@ -1670,7 +1771,7 @@
             try {
                 if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
                 mService.startInputOrWindowGainedFocus(
-                        InputMethodClient.START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+                        StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
                         rootView.getWindowToken(), controlFlags, softInputMode, windowFlags, null,
                         null, 0 /* missingMethodFlags */,
                         rootView.getContext().getApplicationInfo().targetSdkVersion);
@@ -1714,6 +1815,13 @@
      */
     public void updateSelection(View view, int selStart, int selEnd,
             int candidatesStart, int candidatesEnd) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class)
+                    .updateSelection(view, selStart, selEnd, candidatesStart, candidatesEnd);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if ((mServedView != view && (mServedView == null
@@ -1751,6 +1859,12 @@
      * Notify the event when the user tapped or clicked the text view.
      */
     public void viewClicked(View view) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class).viewClicked(view);
+            return;
+        }
+
         final boolean focusChanged = mServedView != mNextServedView;
         checkFocus();
         synchronized (mH) {
@@ -1815,6 +1929,13 @@
      */
     @Deprecated
     public void updateCursor(View view, int left, int top, int right, int bottom) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class)
+                    .updateCursor(view, left, top, right, bottom);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if ((mServedView != view && (mServedView == null
@@ -1846,6 +1967,13 @@
         if (view == null || cursorAnchorInfo == null) {
             return;
         }
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class)
+                    .updateCursorAnchorInfo(view, cursorAnchorInfo);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if ((mServedView != view &&
@@ -1891,6 +2019,13 @@
      * @param data Any data to include with the command.
      */
     public void sendAppPrivateCommand(View view, String action, Bundle data) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(view)) {
+            view.getContext().getSystemService(InputMethodManager.class)
+                    .sendAppPrivateCommand(view, action, data);
+            return;
+        }
+
         checkFocus();
         synchronized (mH) {
             if ((mServedView != view && (mServedView == null
@@ -2062,6 +2197,13 @@
      */
     public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
             @NonNull KeyEvent event) {
+        // Re-dispatch if there is a context mismatch.
+        if (shouldDispatchToViewContext(targetView)) {
+            targetView.getContext().getSystemService(InputMethodManager.class)
+                    .dispatchKeyEventFromInputMethod(targetView, event);
+            return;
+        }
+
         synchronized (mH) {
             ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
             if (viewRootImpl == null) {
@@ -2551,6 +2693,7 @@
         sb.append(",windowFocus=" + view.hasWindowFocus());
         sb.append(",autofillUiShowing=" + isAutofillUIShowing(view));
         sb.append(",window=" + view.getWindowToken());
+        sb.append(",displayId=" + view.getContext().getDisplayId());
         sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
         return sb.toString();
     }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9692579..2e92f14 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.os.LocaleList;
 import android.os.Looper;
@@ -212,34 +211,13 @@
         return suggestSelection(request);
     }
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    @UnsupportedAppUsage
-    default TextSelection suggestSelection(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int selectionStartIndex,
-            @IntRange(from = 0) int selectionEndIndex,
-            @Nullable TextSelection.Options options) {
-        if (options == null) {
-            return suggestSelection(new TextSelection.Request.Builder(
-                    text, selectionStartIndex, selectionEndIndex).build());
-        } else if (options.getRequest() != null) {
-            return suggestSelection(options.getRequest());
-        } else {
-            return suggestSelection(
-                    new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex)
-                            .setDefaultLocales(options.getDefaultLocales())
-                            .build());
-        }
-    }
-
     /**
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param request the text classification request
@@ -262,7 +240,7 @@
      * {@link #classifyText(TextClassification.Request)}. If that method calls this method,
      * a stack overflow error will happen.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param text text providing context for the text to classify (which is specified
@@ -292,34 +270,13 @@
         return classifyText(request);
     }
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    @UnsupportedAppUsage
-    default TextClassification classifyText(
-            @NonNull CharSequence text,
-            @IntRange(from = 0) int startIndex,
-            @IntRange(from = 0) int endIndex,
-            @Nullable TextClassification.Options options) {
-        if (options == null) {
-            return classifyText(
-                    new TextClassification.Request.Builder(text, startIndex, endIndex).build());
-        } else if (options.getRequest() != null) {
-            return classifyText(options.getRequest());
-        } else {
-            return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex)
-                    .setDefaultLocales(options.getDefaultLocales())
-                    .setReferenceTime(options.getReferenceTime())
-                    .build());
-        }
-    }
-
     /**
      * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
      * links information.
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @param request the text links request
@@ -334,27 +291,10 @@
         return new TextLinks.Builder(request.getText().toString()).build();
     }
 
-    // TODO: Remove once apps can build against the latest sdk.
-    /** @hide */
-    @UnsupportedAppUsage
-    default TextLinks generateLinks(
-            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        if (options == null) {
-            return generateLinks(new TextLinks.Request.Builder(text).build());
-        } else if (options.getRequest() != null) {
-            return generateLinks(options.getRequest());
-        } else {
-            return generateLinks(new TextLinks.Request.Builder(text)
-                    .setDefaultLocales(options.getDefaultLocales())
-                    .setEntityConfig(options.getEntityConfig())
-                    .build());
-        }
-    }
-
     /**
      * Returns the maximal length of text that can be processed by generateLinks.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * @see #generateLinks(TextLinks.Request)
@@ -365,9 +305,29 @@
     }
 
     /**
+     * Detects the language of the specified text.
+     *
+     * <p><strong>NOTE: </strong>Call on a worker thread.
+     *
+     *
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
+     * @param request the {@link TextLanguage} request.
+     * @return the {@link TextLanguage} result.
+     */
+    @WorkerThread
+    @NonNull
+    default TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        return TextLanguage.EMPTY;
+    }
+
+    /**
      * Reports a selection event.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      */
     default void onSelectionEvent(@NonNull SelectionEvent event) {}
@@ -375,7 +335,7 @@
     /**
      * Destroys this TextClassifier.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
      * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      *
      * <p>Subsequent calls to this method are no-ops.
@@ -385,7 +345,7 @@
     /**
      * Returns whether or not this TextClassifier has been destroyed.
      *
-     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
+     * <p><strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
      * with the classifier and an attempt to do so would throw an {@link IllegalStateException}.
      * However, this method should never throw an {@link IllegalStateException}.
      *
@@ -396,9 +356,7 @@
     }
 
     /** @hide **/
-    default void dump(@NonNull IndentingPrintWriter printWriter) {
-
-    }
+    default void dump(@NonNull IndentingPrintWriter printWriter) {}
 
     /**
      * Configuration object for specifying what entities to identify.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 3e240cf..7f1e443 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Icon;
+import android.icu.util.ULocale;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -45,6 +46,7 @@
 import com.android.internal.util.Preconditions;
 
 import com.google.android.textclassifier.AnnotatorModel;
+import com.google.android.textclassifier.LangIdModel;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -83,6 +85,9 @@
     private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model";
     private static final String UPDATED_MODEL_FILE_PATH =
             "/data/misc/textclassifier/textclassifier.model";
+    private static final String LANG_ID_MODEL_FILE_PATH = "/etc/textclassifier/lang_id.model";
+    private static final String UPDATED_LANG_ID_MODEL_FILE_PATH =
+            "/data/misc/textclassifier/lang_id.model";
 
     private final Context mContext;
     private final TextClassifier mFallback;
@@ -94,7 +99,9 @@
     @GuardedBy("mLock") // Do not access outside this lock.
     private ModelFile mModel;
     @GuardedBy("mLock") // Do not access outside this lock.
-    private AnnotatorModel mNative;
+    private AnnotatorModel mAnnotatorImpl;
+    @GuardedBy("mLock") // Do not access outside this lock.
+    private LangIdModel mLangIdImpl;
 
     private final Object mLoggerLock = new Object();
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
@@ -127,14 +134,15 @@
                     && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
                 final String localesString = concatenateLocales(request.getDefaultLocales());
                 final ZonedDateTime refTime = ZonedDateTime.now();
-                final AnnotatorModel nativeImpl = getNative(request.getDefaultLocales());
+                final AnnotatorModel annotatorImpl =
+                        getAnnotatorImpl(request.getDefaultLocales());
                 final int start;
                 final int end;
                 if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
                     start = request.getStartIndex();
                     end = request.getEndIndex();
                 } else {
-                    final int[] startEnd = nativeImpl.suggestSelection(
+                    final int[] startEnd = annotatorImpl.suggestSelection(
                             string, request.getStartIndex(), request.getEndIndex(),
                             new AnnotatorModel.SelectionOptions(localesString));
                     start = startEnd[0];
@@ -145,7 +153,7 @@
                         && start <= request.getStartIndex() && end >= request.getEndIndex()) {
                     final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
                     final AnnotatorModel.ClassificationResult[] results =
-                            nativeImpl.classifyText(
+                            annotatorImpl.classifyText(
                                     string, start, end,
                                     new AnnotatorModel.ClassificationOptions(
                                             refTime.toInstant().toEpochMilli(),
@@ -187,7 +195,7 @@
                 final ZonedDateTime refTime = request.getReferenceTime() != null
                         ? request.getReferenceTime() : ZonedDateTime.now();
                 final AnnotatorModel.ClassificationResult[] results =
-                        getNative(request.getDefaultLocales())
+                        getAnnotatorImpl(request.getDefaultLocales())
                                 .classifyText(
                                         string, request.getStartIndex(), request.getEndIndex(),
                                         new AnnotatorModel.ClassificationOptions(
@@ -230,10 +238,10 @@
                     ? request.getEntityConfig().resolveEntityListModifications(
                             getEntitiesForHints(request.getEntityConfig().getHints()))
                     : mSettings.getEntityListDefault();
-            final AnnotatorModel nativeImpl =
-                    getNative(request.getDefaultLocales());
+            final AnnotatorModel annotatorImpl =
+                    getAnnotatorImpl(request.getDefaultLocales());
             final AnnotatorModel.AnnotatedSpan[] annotations =
-                    nativeImpl.annotate(
+                    annotatorImpl.annotate(
                         textString,
                         new AnnotatorModel.AnnotationOptions(
                                 refTime.toInstant().toEpochMilli(),
@@ -288,6 +296,7 @@
         }
     }
 
+    /** @inheritDoc */
     @Override
     public void onSelectionEvent(SelectionEvent event) {
         Preconditions.checkNotNull(event);
@@ -299,7 +308,29 @@
         }
     }
 
-    private AnnotatorModel getNative(LocaleList localeList)
+    /** @inheritDoc */
+    @Override
+    public TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
+        Preconditions.checkNotNull(request);
+        Utils.checkMainThread();
+        try {
+            final TextLanguage.Builder builder = new TextLanguage.Builder();
+            final LangIdModel.LanguageResult[] langResults =
+                    getLangIdImpl().detectLanguages(request.getText().toString());
+            for (int i = 0; i < langResults.length; i++) {
+                builder.putLocale(
+                        ULocale.forLanguageTag(langResults[i].getLanguage()),
+                        langResults[i].getScore());
+            }
+            return builder.build();
+        } catch (Throwable t) {
+            // Avoid throwing from this method. Log the error.
+            Log.e(LOG_TAG, "Error detecting text language.", t);
+        }
+        return mFallback.detectLanguage(request);
+    }
+
+    private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
             throws FileNotFoundException {
         synchronized (mLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
@@ -307,16 +338,72 @@
             if (bestModel == null) {
                 throw new FileNotFoundException("No model for " + localeList.toLanguageTags());
             }
-            if (mNative == null || !Objects.equals(mModel, bestModel)) {
+            if (mAnnotatorImpl == null || !Objects.equals(mModel, bestModel)) {
                 Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
-                destroyNativeIfExistsLocked();
+                destroyAnnotatorImplIfExistsLocked();
                 final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
                         new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
-                mNative = new AnnotatorModel(fd.getFd());
-                closeAndLogError(fd);
-                mModel = bestModel;
+                try {
+                    if (fd != null) {
+                        mAnnotatorImpl = new AnnotatorModel(fd.getFd());
+                        mModel = bestModel;
+                    }
+                } finally {
+                    maybeCloseAndLogError(fd);
+                }
             }
-            return mNative;
+            return mAnnotatorImpl;
+        }
+    }
+
+    @GuardedBy("mLock") // Do not call outside this lock.
+    private void destroyAnnotatorImplIfExistsLocked() {
+        if (mAnnotatorImpl != null) {
+            mAnnotatorImpl.close();
+            mAnnotatorImpl = null;
+        }
+    }
+
+    private LangIdModel getLangIdImpl() throws FileNotFoundException {
+        synchronized (mLock) {
+            if (mLangIdImpl == null) {
+                ParcelFileDescriptor factoryFd = null;
+                ParcelFileDescriptor updateFd = null;
+                try {
+                    int factoryVersion = -1;
+                    int updateVersion = factoryVersion;
+                    final File factoryFile = new File(LANG_ID_MODEL_FILE_PATH);
+                    if (factoryFile.exists()) {
+                        factoryFd = ParcelFileDescriptor.open(
+                                factoryFile, ParcelFileDescriptor.MODE_READ_ONLY);
+                        // TODO: Uncomment when method is implemented:
+                        // if (factoryFd != null) {
+                        //     factoryVersion = LangIdModel.getVersion(factoryFd.getFd());
+                        // }
+                    }
+                    final File updateFile = new File(UPDATED_LANG_ID_MODEL_FILE_PATH);
+                    if (updateFile.exists()) {
+                        updateFd = ParcelFileDescriptor.open(
+                                updateFile, ParcelFileDescriptor.MODE_READ_ONLY);
+                        // TODO: Uncomment when method is implemented:
+                        // if (updateFd != null) {
+                        //     updateVersion = LangIdModel.getVersion(updateFd.getFd());
+                        // }
+                    }
+
+                    if (updateVersion > factoryVersion) {
+                        mLangIdImpl = new LangIdModel(updateFd.getFd());
+                    } else if (factoryFd != null) {
+                        mLangIdImpl = new LangIdModel(factoryFd.getFd());
+                    } else {
+                        throw new FileNotFoundException("Language detection model not found");
+                    }
+                } finally {
+                    maybeCloseAndLogError(factoryFd);
+                    maybeCloseAndLogError(updateFd);
+                }
+            }
+            return mLangIdImpl;
         }
     }
 
@@ -327,14 +414,6 @@
         }
     }
 
-    @GuardedBy("mLock") // Do not call outside this lock.
-    private void destroyNativeIfExistsLocked() {
-        if (mNative != null) {
-            mNative.close();
-            mNative = null;
-        }
-    }
-
     private static String concatenateLocales(@Nullable LocaleList locales) {
         return (locales == null) ? "" : locales.toLanguageTags();
     }
@@ -407,20 +486,19 @@
                 .setText(classifiedText);
 
         final int size = classifications.length;
-        AnnotatorModel.ClassificationResult highestScoringResult = null;
-        float highestScore = Float.MIN_VALUE;
+        AnnotatorModel.ClassificationResult highestScoringResult =
+                size > 0 ? classifications[0] : null;
         for (int i = 0; i < size; i++) {
             builder.setEntityType(classifications[i].getCollection(),
                                   classifications[i].getScore());
-            if (classifications[i].getScore() > highestScore) {
+            if (classifications[i].getScore() > highestScoringResult.getScore()) {
                 highestScoringResult = classifications[i];
-                highestScore = classifications[i].getScore();
             }
         }
 
         boolean isPrimaryAction = true;
         for (LabeledIntent labeledIntent : IntentFactory.create(
-                mContext, referenceTime, highestScoringResult, classifiedText)) {
+                mContext, classifiedText, referenceTime, highestScoringResult)) {
             final RemoteAction action = labeledIntent.asRemoteAction(mContext);
             if (action == null) {
                 continue;
@@ -461,9 +539,13 @@
     }
 
     /**
-     * Closes the ParcelFileDescriptor and logs any errors that occur.
+     * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
      */
-    private static void closeAndLogError(ParcelFileDescriptor fd) {
+    private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
+        if (fd == null) {
+            return;
+        }
+
         try {
             fd.close();
         } catch (IOException e) {
@@ -485,12 +567,17 @@
         /** Returns null if the path did not point to a compatible model. */
         static @Nullable ModelFile fromPath(String path) {
             final File file = new File(path);
+            if (!file.exists()) {
+                return null;
+            }
+            ParcelFileDescriptor modelFd = null;
             try {
-                final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
-                        file, ParcelFileDescriptor.MODE_READ_ONLY);
+                modelFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+                if (modelFd == null) {
+                    return null;
+                }
                 final int version = AnnotatorModel.getVersion(modelFd.getFd());
-                final String supportedLocalesStr =
-                        AnnotatorModel.getLocales(modelFd.getFd());
+                final String supportedLocalesStr = AnnotatorModel.getLocales(modelFd.getFd());
                 if (supportedLocalesStr.isEmpty()) {
                     Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
                     return null;
@@ -500,12 +587,13 @@
                 for (String langTag : supportedLocalesStr.split(",")) {
                     supportedLocales.add(Locale.forLanguageTag(langTag));
                 }
-                closeAndLogError(modelFd);
                 return new ModelFile(path, file.getName(), version, supportedLocales,
                                      languageIndependent);
             } catch (FileNotFoundException e) {
                 Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e);
                 return null;
+            } finally {
+                maybeCloseAndLogError(modelFd);
             }
         }
 
@@ -557,12 +645,12 @@
         public boolean equals(Object other) {
             if (this == other) {
                 return true;
-            } else if (other == null || !ModelFile.class.isAssignableFrom(other.getClass())) {
-                return false;
-            } else {
+            }
+            if (other instanceof ModelFile) {
                 final ModelFile otherModel = (ModelFile) other;
                 return mPath.equals(otherModel.mPath);
             }
+            return false;
         }
 
         @Override
@@ -677,10 +765,12 @@
         @NonNull
         public static List<LabeledIntent> create(
                 Context context,
+                String text,
                 @Nullable Instant referenceTime,
-                AnnotatorModel.ClassificationResult classification,
-                String text) {
-            final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
+                @Nullable AnnotatorModel.ClassificationResult classification) {
+            final String type = classification != null
+                    ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
+                    : null;
             text = text.trim();
             switch (type) {
                 case TextClassifier.TYPE_EMAIL:
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
new file mode 100644
index 0000000..d28459e
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Represents the result of language detection of a piece of text.
+ * <p>
+ * This contains a list of locales, each paired with a confidence score, sorted in decreasing
+ * order of those scores. E.g., for a given input text, the model may return
+ * {@code [<"en", 0.85>, <"fr", 0.15>]}. This sample result means the model reports that it is
+ * 85% likely that the entire text is in English and 15% likely that the entire text is in French,
+ * etc. It does not mean that 85% of the input is in English and 15% is in French.
+ */
+public final class TextLanguage implements Parcelable {
+
+    public static final Creator<TextLanguage> CREATOR = new Creator<TextLanguage>() {
+        @Override
+        public TextLanguage createFromParcel(Parcel in) {
+            return readFromParcel(in);
+        }
+
+        @Override
+        public TextLanguage[] newArray(int size) {
+            return new TextLanguage[size];
+        }
+    };
+
+    static final TextLanguage EMPTY = new Builder().build();
+
+    @Nullable private final String mId;
+    private final EntityConfidence mEntityConfidence;
+    private final Bundle mBundle;
+
+    private TextLanguage(
+            @Nullable String id,
+            EntityConfidence entityConfidence,
+            Bundle bundle) {
+        mId = id;
+        mEntityConfidence = entityConfidence;
+        mBundle = bundle;
+    }
+
+    /**
+     * Returns the id, if one exists, for this object.
+     */
+    @Nullable
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Returns the number of possible locales for the processed text.
+     */
+    @IntRange(from = 0)
+    public int getLocaleHypothesisCount() {
+        return mEntityConfidence.getEntities().size();
+    }
+
+    /**
+     * Returns the language locale at the specified index. Locales are ordered from high
+     * confidence to low confidence.
+     *
+     * @throws IndexOutOfBoundsException if the specified index is out of range.
+     * @see #getLocaleCount() for the number of locales available.
+     */
+    @NonNull
+    public ULocale getLocale(int index) {
+        return ULocale.forLanguageTag(mEntityConfidence.getEntities().get(index));
+    }
+
+    /**
+     * Returns the confidence score for the specified language locale. The value ranges from
+     * 0 (low confidence) to 1 (high confidence). 0 indicates that the locale was not found for
+     * the processed text.
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public float getConfidenceScore(@NonNull ULocale locale) {
+        return mEntityConfidence.getConfidenceScore(locale.toLanguageTag());
+    }
+
+    /**
+     * Returns a bundle containing non-structured extra information about this result.
+     *
+     * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should prefer
+     * to hold a reference to the returned bundle rather than frequently calling this method.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mBundle.deepCopy();
+    }
+
+    @Override
+    public String toString() {
+        return String.format(
+                Locale.US,
+                "TextLanguage {id=%s, locales=%s, bundle=%s}",
+                mId, mEntityConfidence, mBundle);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        mEntityConfidence.writeToParcel(dest, flags);
+        dest.writeBundle(mBundle);
+    }
+
+    private static TextLanguage readFromParcel(Parcel in) {
+        return new TextLanguage(
+                in.readString(),
+                EntityConfidence.CREATOR.createFromParcel(in),
+                in.readBundle());
+    }
+
+    /**
+     * Builder used to build TextLanguage objects.
+     */
+    public static final class Builder {
+
+        @Nullable private String mId;
+        private final Map<String, Float> mEntityConfidenceMap = new ArrayMap<>();
+        @Nullable private Bundle mBundle;
+
+        /**
+         * Sets a language locale for the processed text and assigns a confidence score. If the
+         * locale has already been set, this updates it.
+         *
+         * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
+         *      0 implies the locale does not exist for the processed text.
+         *      Values greater than 1 are clamped to 1.
+         */
+        @NonNull
+        public Builder putLocale(
+                @NonNull ULocale locale,
+                @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
+            Preconditions.checkNotNull(locale);
+            mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
+            return this;
+        }
+
+        /**
+         * Sets an optional id for the TextLanguage object.
+         */
+        @NonNull
+        public Builder setId(@Nullable String id) {
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Sets a bundle containing non-structured extra information about the TextLanguage object.
+         */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle bundle) {
+            mBundle = Preconditions.checkNotNull(bundle);
+            return this;
+        }
+
+        /**
+         * Builds and returns a new TextLanguage object.
+         * <p>
+         * If necessary, this method will verify fields, clamp them, and make them immutable.
+         */
+        @NonNull
+        public TextLanguage build() {
+            mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+            return new TextLanguage(
+                    mId,
+                    new EntityConfidence(mEntityConfidenceMap),
+                    mBundle);
+        }
+    }
+
+    /**
+     * A request object for detecting the language of a piece of text.
+     */
+    public static final class Request implements Parcelable {
+
+        public static final Creator<Request> CREATOR = new Creator<Request>() {
+            @Override
+            public Request createFromParcel(Parcel in) {
+                return readFromParcel(in);
+            }
+
+            @Override
+            public Request[] newArray(int size) {
+                return new Request[size];
+            }
+        };
+
+        private final CharSequence mText;
+        private final Bundle mBundle;
+
+        private Request(CharSequence text, Bundle bundle) {
+            mText = text;
+            mBundle = bundle;
+        }
+
+        /**
+         * Returns the text to process.
+         */
+        @NonNull
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Returns a bundle containing non-structured extra information about this request.
+         *
+         * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+         * prefer to hold a reference to the returned bundle rather than frequently calling this
+         * method.
+         */
+        @NonNull
+        public Bundle getExtras() {
+            return mBundle.deepCopy();
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeCharSequence(mText);
+            dest.writeBundle(mBundle);
+        }
+
+        private static Request readFromParcel(Parcel in) {
+            return new Request(
+                    in.readCharSequence(),
+                    in.readBundle());
+        }
+
+        /**
+         * A builder for building TextLanguage requests.
+         */
+        public static final class Builder {
+
+            private final CharSequence mText;
+            @Nullable private Bundle mBundle;
+
+            /**
+             * Creates a builder to build TextLanguage requests.
+             *
+             * @param text the text to process.
+             */
+            public Builder(@NonNull CharSequence text) {
+                mText = Preconditions.checkNotNull(text);
+            }
+
+            /**
+             * Sets a bundle containing non-structured extra information about the request.
+             */
+            @NonNull
+            public Builder setExtras(@NonNull Bundle bundle) {
+                mBundle = Preconditions.checkNotNull(bundle);
+                return this;
+            }
+
+            /**
+             * Builds and returns a new TextLanguage request object.
+             * <p>
+             * If necessary, this method will verify fields, clamp them, and make them immutable.
+             */
+            @NonNull
+            public Request build() {
+                mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy();
+                return new Request(mText.toString(), mBundle);
+            }
+        }
+    }
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index bdd7a09..300bb6f 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -282,19 +282,28 @@
             SAFE_BROWSING_THREAT_UNKNOWN,
             SAFE_BROWSING_THREAT_MALWARE,
             SAFE_BROWSING_THREAT_PHISHING,
-            SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
+            SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE,
+            SAFE_BROWSING_THREAT_BILLING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SafeBrowsingThreat {}
 
-    /** The resource was blocked for an unknown reason */
+    /** The resource was blocked for an unknown reason. */
     public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0;
-    /** The resource was blocked because it contains malware */
+    /** The resource was blocked because it contains malware. */
     public static final int SAFE_BROWSING_THREAT_MALWARE = 1;
-    /** The resource was blocked because it contains deceptive content */
+    /** The resource was blocked because it contains deceptive content. */
     public static final int SAFE_BROWSING_THREAT_PHISHING = 2;
-    /** The resource was blocked because it contains unwanted software */
+    /** The resource was blocked because it contains unwanted software. */
     public static final int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 3;
+    /**
+     * The resource was blocked because it may trick the user into a billing agreement.
+     *
+     * <p>This constant is only used when targetSdkVersion is at least {@link
+     * android.os.Build.VERSION_CODES#Q}. Otherwise, {@link #SAFE_BROWSING_THREAT_UNKNOWN} is used
+     * instead.
+     */
+    public static final int SAFE_BROWSING_THREAT_BILLING = 4;
 
     /**
      * Report an error to the host application. These errors are unrecoverable
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index ba66571..6ab7f66 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -27,11 +27,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.RecordingCanvas;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.SparseArray;
-import android.view.DisplayListCanvas;
 import android.view.View;
 import android.view.ViewRootImpl;
 
@@ -107,12 +107,12 @@
      * @throws IllegalArgumentException if the canvas is not hardware accelerated
      */
     public void callDrawGlFunction(Canvas canvas, long nativeDrawGLFunctor) {
-        if (!(canvas instanceof DisplayListCanvas)) {
+        if (!(canvas instanceof RecordingCanvas)) {
             // Canvas#isHardwareAccelerated() is only true for subclasses of HardwareCanvas.
             throw new IllegalArgumentException(canvas.getClass().getName()
                     + " is not a DisplayList canvas");
         }
-        ((DisplayListCanvas) canvas).drawGLFunctor2(nativeDrawGLFunctor, null);
+        ((RecordingCanvas) canvas).drawGLFunctor2(nativeDrawGLFunctor, null);
     }
 
     /**
@@ -129,12 +129,12 @@
      */
     public void callDrawGlFunction(@NonNull Canvas canvas, long nativeDrawGLFunctor,
             @Nullable Runnable releasedRunnable) {
-        if (!(canvas instanceof DisplayListCanvas)) {
+        if (!(canvas instanceof RecordingCanvas)) {
             // Canvas#isHardwareAccelerated() is only true for subclasses of HardwareCanvas.
             throw new IllegalArgumentException(canvas.getClass().getName()
                     + " is not a DisplayList canvas");
         }
-        ((DisplayListCanvas) canvas).drawGLFunctor2(nativeDrawGLFunctor, releasedRunnable);
+        ((RecordingCanvas) canvas).drawGLFunctor2(nativeDrawGLFunctor, releasedRunnable);
     }
 
     /**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 8027dd7..48c164f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -42,8 +42,10 @@
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.RenderNode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -83,7 +85,6 @@
 import android.view.ActionMode.Callback;
 import android.view.ContextMenu;
 import android.view.ContextThemeWrapper;
-import android.view.DisplayListCanvas;
 import android.view.DragAndDropPermissions;
 import android.view.DragEvent;
 import android.view.Gravity;
@@ -93,7 +94,6 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import android.view.RenderNode;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.View.DragShadowBuilder;
@@ -1941,18 +1941,18 @@
 
             // Rebuild display list if it is invalid
             if (blockDisplayListIsInvalid) {
-                final DisplayListCanvas displayListCanvas = blockDisplayList.start(
+                final RecordingCanvas recordingCanvas = blockDisplayList.start(
                         right - left, bottom - top);
                 try {
                     // drawText is always relative to TextView's origin, this translation
                     // brings this range of text back to the top left corner of the viewport
-                    displayListCanvas.translate(-left, -top);
-                    layout.drawText(displayListCanvas, blockBeginLine, blockEndLine);
+                    recordingCanvas.translate(-left, -top);
+                    layout.drawText(recordingCanvas, blockBeginLine, blockEndLine);
                     mTextRenderNodes[blockIndex].isDirty = false;
                     // No need to untranslate, previous context is popped after
                     // drawDisplayList
                 } finally {
-                    blockDisplayList.end(displayListCanvas);
+                    blockDisplayList.end(recordingCanvas);
                     // Same as drawDisplayList below, handled by our TextView's parent
                     blockDisplayList.setClipToBounds(false);
                 }
@@ -1962,7 +1962,7 @@
             blockDisplayList.setLeftTopRightBottom(left, top, right, bottom);
             mTextRenderNodes[blockIndex].needsToBeShifted = false;
         }
-        ((DisplayListCanvas) canvas).drawRenderNode(blockDisplayList);
+        ((RecordingCanvas) canvas).drawRenderNode(blockDisplayList);
         return startIndexToFindAvailableRenderNode;
     }
 
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 16ddd0f..6a3fc0f 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -33,15 +33,15 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
-import android.view.DisplayListCanvas;
 import android.view.PixelCopy;
-import android.view.RenderNode;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
@@ -704,7 +704,7 @@
                     cornerRadius
             );
 
-            final DisplayListCanvas canvas = mRenderer.getRootNode().start(width, height);
+            final RecordingCanvas canvas = mRenderer.getRootNode().start(width, height);
             try {
                 canvas.insertReorderBarrier();
                 canvas.drawRenderNode(mBitmapRenderNode);
@@ -736,7 +736,7 @@
             bitmapRenderNode.setClipToOutline(true);
 
             // Create a dummy draw, which will be replaced later with real drawing.
-            final DisplayListCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight);
+            final RecordingCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight);
             try {
                 canvas.drawColor(0xFF00FF00);
             } finally {
@@ -817,7 +817,7 @@
                     return;
                 }
 
-                final DisplayListCanvas canvas =
+                final RecordingCanvas canvas =
                         mBitmapRenderNode.start(mContentWidth, mContentHeight);
                 try {
                     canvas.drawColor(Color.WHITE);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8f17e96..4d03123 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -370,10 +370,12 @@
                 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
                 Context context = view.getContext();
                 ActivityOptions opts = getActivityOptions(context);
+                // The NEW_TASK flags are applied through the activity options and not as a part of
+                // the call to startIntentSender() to ensure that they are consistently applied to
+                // both mutable and immutable PendingIntents.
                 context.startIntentSender(
                         pendingIntent.getIntentSender(), fillInIntent,
-                        Intent.FLAG_ACTIVITY_NEW_TASK,
-                        Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
+                        0, 0, 0, opts.toBundle());
             } catch (IntentSender.SendIntentException e) {
                 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
                 return false;
@@ -401,10 +403,15 @@
                 windowAnimationStyle.recycle();
 
                 if (enterAnimationId != 0) {
-                    return ActivityOptions.makeCustomAnimation(context, enterAnimationId, 0);
+                    final ActivityOptions opts = ActivityOptions.makeCustomAnimation(context,
+                            enterAnimationId, 0);
+                    opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    return opts;
                 }
             }
-            return ActivityOptions.makeBasic();
+            final ActivityOptions opts = ActivityOptions.makeBasic();
+            opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            return opts;
         }
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f74c234..66809db 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -63,7 +63,7 @@
 import android.graphics.RectF;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
-import android.graphics.fonts.Font;
+import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.icu.text.DecimalFormatSymbols;
 import android.os.AsyncTask;
@@ -2073,7 +2073,7 @@
      */
     private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
             @XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
-            @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
+            @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int weight) {
         if (typeface == null && familyName != null) {
             // Lookup normal Typeface from system font map.
             final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
@@ -2100,9 +2100,9 @@
     }
 
     private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
-            @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
+            @IntRange(from = -1, to = FontStyle.FONT_WEIGHT_MAX) int weight) {
         if (weight >= 0) {
-            weight = Math.min(Font.FONT_WEIGHT_MAX, weight);
+            weight = Math.min(FontStyle.FONT_WEIGHT_MAX, weight);
             final boolean italic = (style & Typeface.ITALIC) != 0;
             setTypeface(Typeface.create(typeface, weight, italic));
         } else {
@@ -3517,6 +3517,7 @@
         ColorStateList mTextColorHint = null;
         ColorStateList mTextColorLink = null;
         int mTextSize = -1;
+        LocaleList mTextLocales = null;
         String mFontFamily = null;
         Typeface mFontTypeface = null;
         boolean mFontFamilyExplicit = false;
@@ -3543,6 +3544,7 @@
                     + "    mTextColorHint:" + mTextColorHint + "\n"
                     + "    mTextColorLink:" + mTextColorLink + "\n"
                     + "    mTextSize:" + mTextSize + "\n"
+                    + "    mTextLocales:" + mTextLocales + "\n"
                     + "    mFontFamily:" + mFontFamily + "\n"
                     + "    mFontTypeface:" + mFontTypeface + "\n"
                     + "    mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
@@ -3579,6 +3581,8 @@
                 com.android.internal.R.styleable.TextAppearance_textColorLink);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textSize,
                 com.android.internal.R.styleable.TextAppearance_textSize);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textLocale,
+                com.android.internal.R.styleable.TextAppearance_textLocale);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_typeface,
                 com.android.internal.R.styleable.TextAppearance_typeface);
         sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFamily,
@@ -3652,6 +3656,15 @@
                     attributes.mTextSize =
                             appearance.getDimensionPixelSize(attr, attributes.mTextSize);
                     break;
+                case com.android.internal.R.styleable.TextAppearance_textLocale:
+                    final String localeString = appearance.getString(attr);
+                    if (localeString != null) {
+                        final LocaleList localeList = LocaleList.forLanguageTags(localeString);
+                        if (!localeList.isEmpty()) {
+                            attributes.mTextLocales = localeList;
+                        }
+                    }
+                    break;
                 case com.android.internal.R.styleable.TextAppearance_typeface:
                     attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex);
                     if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
@@ -3738,6 +3751,10 @@
             setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
         }
 
+        if (attributes.mTextLocales != null) {
+            setTextLocales(attributes.mTextLocales);
+        }
+
         if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
             attributes.mFontFamily = null;
         }
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 10cf702..c256d57 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -137,7 +137,7 @@
         String pkg = mContext.getOpPackageName();
         TN tn = mTN;
         tn.mNextView = mNextView;
-        final int displayId = mContext.getDisplay().getDisplayId();
+        final int displayId = mContext.getDisplayId();
 
         try {
             service.enqueueToast(pkg, tn, mDuration, displayId);
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 0f8295a..7c371cb 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -31,6 +31,8 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 import java.util.Set;
 
@@ -42,6 +44,14 @@
 
     private static final String TAG = "AssistUtils";
 
+    /**
+     * Sentinel value for "no default assistant specified."
+     *
+     * Empty string is already used to represent an explicit setting of No Assistant. null cannot
+     * be used because we can't represent a null value in XML.
+     */
+    private static final String UNSET = "#+UNSET";
+
     private final Context mContext;
     private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
 
@@ -178,10 +188,21 @@
             return ComponentName.unflattenFromString(setting);
         }
 
+        final String defaultSetting = mContext.getResources().getString(
+                R.string.config_defaultAssistantComponentName);
+        if (defaultSetting != null && !defaultSetting.equals(UNSET)) {
+            return ComponentName.unflattenFromString(defaultSetting);
+        }
+
         // Fallback to keep backward compatible behavior when there is no user setting.
         if (activeServiceSupportsAssistGesture()) {
             return getActiveServiceComponentName();
         }
+
+        if (UNSET.equals(defaultSetting)) {
+            return null;
+        }
+
         final SearchManager searchManager =
                 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
         if (searchManager == null) {
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index b8a2dff..c0c689c 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -52,4 +52,6 @@
 
     /** For both ...Intent and ...Service based usage */
     boolean isRecognitionActive(in ParcelUuid parcelUuid);
+
+    SoundTrigger.RecognitionEvent getModelState(in ParcelUuid parcelUuid);
 }
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index a8edfb6..498de53 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -16,12 +16,17 @@
 
 package com.android.internal.app;
 
+import static android.content.res.ResourceId.ID_NULL;
+
 import android.Manifest;
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -31,16 +36,19 @@
 
 public class SuspendedAppActivity extends AlertActivity
         implements DialogInterface.OnClickListener {
-    private static final String TAG = "SuspendedAppActivity";
-    public static final String EXTRA_SUSPENDED_PACKAGE =
-            "SuspendedAppActivity.extra.SUSPENDED_PACKAGE";
+    private static final String TAG = SuspendedAppActivity.class.getSimpleName();
+    private static final String PACKAGE_NAME = "com.android.internal.app";
+
+    public static final String EXTRA_SUSPENDED_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDED_PACKAGE";
     public static final String EXTRA_SUSPENDING_PACKAGE =
-            "SuspendedAppActivity.extra.SUSPENDING_PACKAGE";
-    public static final String EXTRA_DIALOG_MESSAGE = "SuspendedAppActivity.extra.DIALOG_MESSAGE";
+            PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE";
+    public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO";
 
     private Intent mMoreDetailsIntent;
     private int mUserId;
     private PackageManager mPm;
+    private Resources mSuspendingAppResources;
+    private SuspendDialogInfo mSuppliedDialogInfo;
 
     private CharSequence getAppLabel(String packageName) {
         try {
@@ -66,6 +74,65 @@
         return null;
     }
 
+    private Drawable resolveIcon() {
+        final int iconId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getIconResId()
+                : ID_NULL;
+        if (iconId != ID_NULL && mSuspendingAppResources != null) {
+            try {
+                return mSuspendingAppResources.getDrawable(iconId, null);
+            } catch (Resources.NotFoundException nfe) {
+                Slog.e(TAG, "Could not resolve drawable resource id " + iconId);
+            }
+        }
+        return null;
+    }
+
+    private String resolveTitle() {
+        final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId()
+                : ID_NULL;
+        if (titleId != ID_NULL && mSuspendingAppResources != null) {
+            try {
+                return mSuspendingAppResources.getString(titleId);
+            } catch (Resources.NotFoundException nfe) {
+                Slog.e(TAG, "Could not resolve string resource id " + titleId);
+            }
+        }
+        return getString(R.string.app_suspended_title);
+    }
+
+    private String resolveDialogMessage(String suspendingPkg, String suspendedPkg) {
+        final CharSequence suspendedAppLabel = getAppLabel(suspendedPkg);
+        if (mSuppliedDialogInfo != null) {
+            final int messageId = mSuppliedDialogInfo.getDialogMessageResId();
+            final String message = mSuppliedDialogInfo.getDialogMessage();
+            if (messageId != ID_NULL && mSuspendingAppResources != null) {
+                try {
+                    return mSuspendingAppResources.getString(messageId, suspendedAppLabel);
+                } catch (Resources.NotFoundException nfe) {
+                    Slog.e(TAG, "Could not resolve string resource id " + messageId);
+                }
+            } else if (message != null) {
+                return String.format(getResources().getConfiguration().getLocales().get(0), message,
+                        suspendedAppLabel);
+            }
+        }
+        return getString(R.string.app_suspended_default_message, suspendedAppLabel,
+                getAppLabel(suspendingPkg));
+    }
+
+    private String resolveNeutralButtonText() {
+        final int buttonTextId = (mSuppliedDialogInfo != null)
+                ? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL;
+        if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
+            try {
+                return mSuspendingAppResources.getString(buttonTextId);
+            } catch (Resources.NotFoundException nfe) {
+                Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
+            }
+        }
+        return getString(R.string.app_suspended_more_details);
+    }
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -79,26 +146,26 @@
             finish();
             return;
         }
-        final String suppliedMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
         final String suspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE);
         final String suspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE);
-        final CharSequence suspendedAppLabel = getAppLabel(suspendedPackage);
-        final CharSequence dialogMessage;
-        if (suppliedMessage == null) {
-            dialogMessage = getString(R.string.app_suspended_default_message, suspendedAppLabel,
-                    getAppLabel(suspendingPackage));
-        } else {
-            dialogMessage = String.format(getResources().getConfiguration().getLocales().get(0),
-                    suppliedMessage, suspendedAppLabel);
+        mSuppliedDialogInfo = intent.getParcelableExtra(EXTRA_DIALOG_INFO);
+        if (mSuppliedDialogInfo != null) {
+            try {
+                mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(suspendingPackage,
+                        mUserId);
+            } catch (PackageManager.NameNotFoundException ne) {
+                Slog.e(TAG, "Could not find resources for " + suspendingPackage, ne);
+            }
         }
 
         final AlertController.AlertParams ap = mAlertParams;
-        ap.mTitle = getString(R.string.app_suspended_title);
-        ap.mMessage = dialogMessage;
+        ap.mIcon = resolveIcon();
+        ap.mTitle = resolveTitle();
+        ap.mMessage = resolveDialogMessage(suspendingPackage, suspendedPackage);
         ap.mPositiveButtonText = getString(android.R.string.ok);
         mMoreDetailsIntent = getMoreDetailsActivity(suspendingPackage, suspendedPackage, mUserId);
         if (mMoreDetailsIntent != null) {
-            ap.mNeutralButtonText = getString(R.string.app_suspended_more_details);
+            ap.mNeutralButtonText = resolveNeutralButtonText();
         }
         ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
         setupAlert();
@@ -116,11 +183,11 @@
     }
 
     public static Intent createSuspendedAppInterceptIntent(String suspendedPackage,
-            String suspendingPackage, String dialogMessage, int userId) {
+            String suspendingPackage, SuspendDialogInfo dialogInfo, int userId) {
         return new Intent()
                 .setClassName("android", SuspendedAppActivity.class.getName())
                 .putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
-                .putExtra(EXTRA_DIALOG_MESSAGE, dialogMessage)
+                .putExtra(EXTRA_DIALOG_INFO, dialogInfo)
                 .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
                 .putExtra(Intent.EXTRA_USER_ID, userId)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
new file mode 100644
index 0000000..a5dc3d1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+
+/**
+ * Provides useful methods for debugging.
+ */
+public final class InputMethodDebug {
+    /**
+     * Not intended to be instantiated.
+     */
+    private InputMethodDebug() {
+    }
+
+    /**
+     * Converts {@link StartInputReason} to {@link String} for debug logging.
+     *
+     * @param reason integer constant for {@link StartInputReason}.
+     * @return {@link String} message corresponds for the given {@code reason}.
+     */
+    public static String startInputReasonToString(@StartInputReason int reason) {
+        switch (reason) {
+            case StartInputReason.UNSPECIFIED:
+                return "UNSPECIFIED";
+            case StartInputReason.WINDOW_FOCUS_GAIN:
+                return "WINDOW_FOCUS_GAIN";
+            case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY:
+                return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
+            case StartInputReason.APP_CALLED_RESTART_INPUT_API:
+                return "APP_CALLED_RESTART_INPUT_API";
+            case StartInputReason.CHECK_FOCUS:
+                return "CHECK_FOCUS";
+            case StartInputReason.BOUND_TO_IMMS:
+                return "BOUND_TO_IMMS";
+            case StartInputReason.UNBOUND_FROM_IMMS:
+                return "UNBOUND_FROM_IMMS";
+            case StartInputReason.ACTIVATED_BY_IMMS:
+                return "ACTIVATED_BY_IMMS";
+            case StartInputReason.DEACTIVATED_BY_IMMS:
+                return "DEACTIVATED_BY_IMMS";
+            case StartInputReason.SESSION_CREATED_BY_IME:
+                return "SESSION_CREATED_BY_IME";
+            default:
+                return "Unknown=" + reason;
+        }
+    }
+
+    /**
+     * Converts {@link UnbindReason} to {@link String} for debug logging.
+     *
+     * @param reason integer constant for {@link UnbindReason}.
+     * @return {@link String} message corresponds for the given {@code reason}.
+     */
+    public static String unbindReasonToString(@UnbindReason int reason) {
+        switch (reason) {
+            case UnbindReason.UNSPECIFIED:
+                return "UNSPECIFIED";
+            case UnbindReason.SWITCH_CLIENT:
+                return "SWITCH_CLIENT";
+            case UnbindReason.SWITCH_IME:
+                return "SWITCH_IME";
+            case UnbindReason.DISCONNECT_IME:
+                return "DISCONNECT_IME";
+            case UnbindReason.NO_IME:
+                return "NO_IME";
+            case UnbindReason.SWITCH_IME_FAILED:
+                return "SWITCH_IME_FAILED";
+            case UnbindReason.SWITCH_USER:
+                return "SWITCH_USER";
+            default:
+                return "Unknown=" + reason;
+        }
+    }
+
+    /**
+     * Converts {@link SoftInputModeFlags} to {@link String} for debug logging.
+     *
+     * @param softInputMode integer constant for {@link SoftInputModeFlags}.
+     * @return {@link String} message corresponds for the given {@code softInputMode}.
+     */
+    public static String softInputModeToString(@SoftInputModeFlags int softInputMode) {
+        final StringBuilder sb = new StringBuilder();
+        final int state = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        final int adjust = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+        final boolean isForwardNav =
+                (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
+
+        switch (state) {
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
+                sb.append("STATE_UNSPECIFIED");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+                sb.append("STATE_UNCHANGED");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+                sb.append("STATE_HIDDEN");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+                sb.append("STATE_ALWAYS_HIDDEN");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+                sb.append("STATE_VISIBLE");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
+                sb.append("STATE_ALWAYS_VISIBLE");
+                break;
+            default:
+                sb.append("STATE_UNKNOWN(");
+                sb.append(state);
+                sb.append(")");
+                break;
+        }
+
+        switch (adjust) {
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
+                sb.append("|ADJUST_UNSPECIFIED");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
+                sb.append("|ADJUST_RESIZE");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN:
+                sb.append("|ADJUST_PAN");
+                break;
+            case WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
+                sb.append("|ADJUST_NOTHING");
+                break;
+            default:
+                sb.append("|ADJUST_UNKNOWN(");
+                sb.append(adjust);
+                sb.append(")");
+                break;
+        }
+
+        if (isForwardNav) {
+            // This is a special bit that is set by the system only during the window navigation.
+            sb.append("|IS_FORWARD_NAVIGATION");
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java
new file mode 100644
index 0000000..a01c459
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/StartInputReason.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link android.view.inputmethod.InputMethodManager} is calling
+ * {@link com.android.internal.view.IInputMethodManager#startInputOrWindowGainedFocus}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+        StartInputReason.UNSPECIFIED,
+        StartInputReason.WINDOW_FOCUS_GAIN,
+        StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY,
+        StartInputReason.APP_CALLED_RESTART_INPUT_API,
+        StartInputReason.CHECK_FOCUS,
+        StartInputReason.BOUND_TO_IMMS,
+        StartInputReason.UNBOUND_FROM_IMMS,
+        StartInputReason.ACTIVATED_BY_IMMS,
+        StartInputReason.DEACTIVATED_BY_IMMS,
+        StartInputReason.SESSION_CREATED_BY_IME})
+public @interface StartInputReason {
+    /**
+     * Reason is not specified.
+     */
+    int UNSPECIFIED = 0;
+    /**
+     * {@link android.view.Window} gained focus and it made the focused {@link android.view.View}
+     * to (re)start a new connection.
+     */
+    int WINDOW_FOCUS_GAIN = 1;
+    /**
+     * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is
+     * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports
+     * this window focus change event.
+     */
+    int WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is
+     * either explicitly called by the application or indirectly called by some Framework class
+     * (e.g. {@link android.widget.EditText}).
+     */
+    int APP_CALLED_RESTART_INPUT_API = 3;
+    /**
+     * {@link android.view.View} requested a new connection because of view focus change.
+     */
+    int CHECK_FOCUS = 4;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#onBindMethod}.
+     */
+    int BOUND_TO_IMMS = 5;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+     */
+    int UNBOUND_FROM_IMMS = 6;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#setActive}.
+     */
+    int ACTIVATED_BY_IMMS = 7;
+    /**
+     * {@link android.view.inputmethod.InputMethodManager} is responding to
+     * {@link com.android.internal.view.IInputMethodClient#setActive}.
+     */
+    int DEACTIVATED_BY_IMMS = 8;
+    /**
+     * {@link com.android.server.inputmethod.InputMethodManagerService} is responding to
+     * {@link com.android.internal.view.IInputSessionCallback#sessionCreated}.
+     */
+    int SESSION_CREATED_BY_IME = 9;
+}
diff --git a/core/java/com/android/internal/inputmethod/UnbindReason.java b/core/java/com/android/internal/inputmethod/UnbindReason.java
new file mode 100644
index 0000000..f0f18f1
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/UnbindReason.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why {@link com.android.server.inputmethod.InputMethodManagerService} is
+ * calling {@link com.android.internal.view.IInputMethodClient#onUnbindMethod}.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+        UnbindReason.UNSPECIFIED,
+        UnbindReason.SWITCH_CLIENT,
+        UnbindReason.SWITCH_IME,
+        UnbindReason.DISCONNECT_IME,
+        UnbindReason.NO_IME,
+        UnbindReason.SWITCH_IME_FAILED,
+        UnbindReason.SWITCH_USER})
+public @interface UnbindReason {
+    /**
+     * Reason is not specified.
+     */
+    int UNSPECIFIED = 0;
+    /**
+     * When a new IME client becomes active, the previous IME client will unbound from the current
+     * IME.
+     */
+    int SWITCH_CLIENT = 1;
+    /**
+     * Before a new IME becomes active, the current IME client be unbound from the previous IME.
+     */
+    int SWITCH_IME = 2;
+    /**
+     * When the current IME is disconnected, the current IME client will be unbound.
+     */
+    int DISCONNECT_IME = 3;
+    /**
+     * When the system loses the last enabled IME, the current IME client will be unbound.
+     */
+    int NO_IME = 4;
+    /**
+     * When the system failed to switch to another IME, the current IME client will be unbound.
+     */
+    int SWITCH_IME_FAILED = 5;
+    /**
+     * When a new user becomes foreground, the previous IME client will be unbound from the previous
+     * user's active IME.
+     */
+    int SWITCH_USER = 6;
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 46667d1..2442d0b 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -158,7 +158,7 @@
                                          PROC_WAKELOCKS_FORMAT,
                         nameStringArray, wlData, null);
 
-                name = nameStringArray[0];
+                name = nameStringArray[0].trim();
                 count = (int) wlData[1];
 
                 if (wakeup_sources) {
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 8dc97fe..2b661c2 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -49,6 +49,7 @@
     private final int mEntriesSizeCap;
     private int mSamplingInterval;
     private CachedDeviceState.Readonly mDeviceState;
+    private long mStartTime = System.currentTimeMillis();
 
     public LooperStats(int samplingInterval, int entriesSizeCap) {
         this.mSamplingInterval = samplingInterval;
@@ -144,6 +145,11 @@
         return exportedEntries;
     }
 
+    /** Returns a timestamp indicating when the statistics were last reset. */
+    public long getStartTimeMillis() {
+        return mStartTime;
+    }
+
     private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
         synchronized (specialEntry) {
             if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
@@ -163,6 +169,7 @@
         synchronized (mOverflowEntry) {
             mOverflowEntry.reset();
         }
+        mStartTime = System.currentTimeMillis();
     }
 
     public void setSamplingInterval(int samplingInterval) {
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index a70209c..f14007b 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -16,13 +16,13 @@
 
 package com.android.internal.policy;
 
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Looper;
 import android.view.Choreographer;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
 import android.view.ThreadedRenderer;
 
 /**
@@ -339,7 +339,7 @@
         mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
 
         // Draw the caption and content backdrops in to our render node.
-        DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
+        RecordingCanvas canvas = mFrameAndBackdropNode.start(width, height);
         final Drawable drawable = mUserCaptionBackgroundDrawable != null
                 ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
 
@@ -368,7 +368,7 @@
         if (mSystemBarBackgroundNode == null) {
             return;
         }
-        DisplayListCanvas canvas = mSystemBarBackgroundNode.start(width, height);
+        RecordingCanvas canvas = mSystemBarBackgroundNode.start(width, height);
         mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
         final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top);
         if (mStatusBarColor != null) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 4697266..94140ab 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -16,76 +16,6 @@
 
 package com.android.internal.policy;
 
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.app.WindowConfiguration;
-import android.graphics.Outline;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.util.Pair;
-import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityNodeInfo;
-import com.android.internal.R;
-import com.android.internal.policy.PhoneWindow.PanelFeatureState;
-import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
-import com.android.internal.view.FloatingActionMode;
-import com.android.internal.view.RootViewSurfaceTaker;
-import com.android.internal.view.StandaloneActionMode;
-import com.android.internal.view.menu.ContextMenuBuilder;
-import com.android.internal.view.menu.MenuHelper;
-import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.BackgroundFallback;
-import com.android.internal.widget.DecorCaptionView;
-import com.android.internal.widget.FloatingToolbar;
-
-import java.util.List;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Shader;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ActionMode;
-import android.view.ContextThemeWrapper;
-import android.view.DisplayListCanvas;
-import android.view.Gravity;
-import android.view.InputQueue;
-import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.ThreadedRenderer;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.ViewTreeObserver;
-import android.view.Window;
-import android.view.WindowCallbacks;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.PopupWindow;
-
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -108,8 +38,79 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+
 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Pair;
+import android.util.TypedValue;
+import android.view.ActionMode;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.InputQueue;
+import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.ThreadedRenderer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowCallbacks;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow;
+
+import com.android.internal.R;
+import com.android.internal.policy.PhoneWindow.PanelFeatureState;
+import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
+import com.android.internal.view.FloatingActionMode;
+import com.android.internal.view.RootViewSurfaceTaker;
+import com.android.internal.view.StandaloneActionMode;
+import com.android.internal.view.menu.ContextMenuBuilder;
+import com.android.internal.view.menu.MenuHelper;
+import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.BackgroundFallback;
+import com.android.internal.widget.DecorCaptionView;
+import com.android.internal.widget.FloatingToolbar;
+
+import java.util.List;
+
 /** @hide */
 public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
     private static final String TAG = "DecorView";
@@ -2134,7 +2135,7 @@
     }
 
     @Override
-    public void onPostDraw(DisplayListCanvas canvas) {
+    public void onPostDraw(RecordingCanvas canvas) {
         drawResizingShadowIfNeeded(canvas);
     }
 
@@ -2152,7 +2153,7 @@
                 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
     }
 
-    private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
+    private void drawResizingShadowIfNeeded(RecordingCanvas canvas) {
         if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
                 || mWindow.isTranslucent()
                 || mWindow.isShowingWallpaper()) {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 8751517..3b7ce0a 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -1387,7 +1387,7 @@
     private int getOptionsPanelGravity() {
         try {
             return WindowManagerHolder.sWindowManager.getPreferredOptionsPanelGravity(
-                    getContext().getDisplay().getDisplayId());
+                    getContext().getDisplayId());
         } catch (RemoteException ex) {
             Log.e(TAG, "Couldn't getOptionsPanelGravity; using default", ex);
             return Gravity.CENTER | Gravity.BOTTOM;
@@ -3642,7 +3642,7 @@
                 if (!mIsWatching) {
                     try {
                         WindowManagerHolder.sWindowManager.watchRotation(this,
-                                phoneWindow.getContext().getDisplay().getDisplayId());
+                                phoneWindow.getContext().getDisplayId());
                         mHandler = new Handler();
                         mIsWatching = true;
                     } catch (RemoteException ex) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 2618356..17b2bc4 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -24,7 +24,6 @@
  */
 oneway interface IInputMethodClient {
     void onBindMethod(in InputBindResult res);
-    // unbindReason corresponds to InputMethodClient.UnbindReason.
     void onUnbindMethod(int sequence, int unbindReason);
     void setActive(boolean active, boolean fullscreen);
     void reportFullscreenMode(boolean fullscreen);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5f1243f..29c55c2 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -31,7 +31,8 @@
  * applications.
  */
 interface IInputMethodManager {
-    void addClient(in IInputMethodClient client, in IInputContext inputContext);
+    void addClient(in IInputMethodClient client, in IInputContext inputContext,
+            int untrustedDisplayId);
 
     // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getInputMethodList();
@@ -45,7 +46,6 @@
     // Currently there is a bug that aidl doesn't accept List<Parcelable>
     List getShortcutInputMethodsAndSubtypes();
 
-    void finishInput(in IInputMethodClient client);
     boolean showSoftInput(in IInputMethodClient client, int flags,
             in ResultReceiver resultReceiver);
     boolean hideSoftInput(in IInputMethodClient client, int flags,
@@ -54,7 +54,7 @@
     // has gained focus, and if 'attribute' is non-null then also does startInput.
     // @NonNull
     InputBindResult startInputOrWindowGainedFocus(
-            /* @InputMethodClient.StartInputReason */ int startInputReason,
+            /* @StartInputReason */ int startInputReason,
             in IInputMethodClient client, in IBinder windowToken, int controlFlags,
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, IInputContext inputContext,
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 367b713..794238a 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -29,10 +29,8 @@
  * {@hide}
  */
 oneway interface IInputMethodSession {
-    void finishInput();
-
     void updateExtractedText(int token, in ExtractedText text);
-    
+
     void updateSelection(int oldSelStart, int oldSelEnd,
             int newSelStart, int newSelEnd,
             int candidatesStart, int candidatesEnd);
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 101fd41..ec8e8da 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -51,6 +51,9 @@
             ResultCode.ERROR_INVALID_USER,
             ResultCode.ERROR_NULL_EDITOR_INFO,
             ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
+            ResultCode.ERROR_NO_EDITOR,
+            ResultCode.ERROR_DISPLAY_ID_MISMATCH,
+            ResultCode.ERROR_INVALID_DISPLAY_ID,
     })
     public @interface ResultCode {
         /**
@@ -139,13 +142,22 @@
          * The client should try to restart input when its {@link android.view.Window} is focused
          * again.</p>
          *
-         * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int)
+         * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int)
          */
         int ERROR_NOT_IME_TARGET_WINDOW = 11;
         /**
          * Indicates that focused view in the current window is not an editor.
          */
         int ERROR_NO_EDITOR = 12;
+        /**
+         * Indicates that there is a mismatch in display ID between IME client and focused Window.
+         */
+        int ERROR_DISPLAY_ID_MISMATCH = 13;
+        /**
+         * Indicates that current IME client is no longer allowed to access to the associated
+         * display.
+         */
+        int ERROR_INVALID_DISPLAY_ID = 14;
     }
 
     @ResultCode
@@ -271,6 +283,10 @@
                 return "ERROR_NULL_EDITOR_INFO";
             case ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
                 return "ERROR_NOT_IME_TARGET_WINDOW";
+            case ResultCode.ERROR_DISPLAY_ID_MISMATCH:
+                return "ERROR_DISPLAY_ID_MISMATCH";
+            case ResultCode.ERROR_INVALID_DISPLAY_ID:
+                return "ERROR_INVALID_DISPLAY_ID";
             default:
                 return "Unknown(" + result + ")";
         }
@@ -316,4 +332,15 @@
      */
     public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER);
 
+    /**
+     * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}.
+     */
+    public static final InputBindResult DISPLAY_ID_MISMATCH =
+            error(ResultCode.ERROR_DISPLAY_ID_MISMATCH);
+
+    /**
+     * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}.
+     */
+    public static final InputBindResult INVALID_DISPLAY_ID =
+            error(ResultCode.ERROR_INVALID_DISPLAY_ID);
 }
diff --git a/core/java/com/android/internal/view/InputMethodClient.java b/core/java/com/android/internal/view/InputMethodClient.java
deleted file mode 100644
index bbd33a2..0000000
--- a/core/java/com/android/internal/view/InputMethodClient.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.view;
-
-import android.annotation.IntDef;
-import android.view.WindowManager.LayoutParams;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-
-import java.lang.annotation.Retention;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-public final class InputMethodClient {
-    public static final int START_INPUT_REASON_UNSPECIFIED = 0;
-    public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN = 1;
-    public static final int START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY = 2;
-    public static final int START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API = 3;
-    public static final int START_INPUT_REASON_CHECK_FOCUS = 4;
-    public static final int START_INPUT_REASON_BOUND_TO_IMMS = 5;
-    public static final int START_INPUT_REASON_UNBOUND_FROM_IMMS = 6;
-    public static final int START_INPUT_REASON_ACTIVATED_BY_IMMS = 7;
-    public static final int START_INPUT_REASON_DEACTIVATED_BY_IMMS = 8;
-    public static final int START_INPUT_REASON_SESSION_CREATED_BY_IME = 9;
-
-    @Retention(SOURCE)
-    @IntDef({START_INPUT_REASON_UNSPECIFIED, START_INPUT_REASON_WINDOW_FOCUS_GAIN,
-            START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY,
-            START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API, START_INPUT_REASON_CHECK_FOCUS,
-            START_INPUT_REASON_BOUND_TO_IMMS, START_INPUT_REASON_ACTIVATED_BY_IMMS,
-            START_INPUT_REASON_DEACTIVATED_BY_IMMS, START_INPUT_REASON_SESSION_CREATED_BY_IME})
-    public @interface StartInputReason {}
-
-    public static String getStartInputReason(@StartInputReason final int reason) {
-        switch (reason) {
-            case START_INPUT_REASON_UNSPECIFIED:
-                return "UNSPECIFIED";
-            case START_INPUT_REASON_WINDOW_FOCUS_GAIN:
-                return "WINDOW_FOCUS_GAIN";
-            case START_INPUT_REASON_WINDOW_FOCUS_GAIN_REPORT_ONLY:
-                return "WINDOW_FOCUS_GAIN_REPORT_ONLY";
-            case START_INPUT_REASON_APP_CALLED_RESTART_INPUT_API:
-                return "APP_CALLED_RESTART_INPUT_API";
-            case START_INPUT_REASON_CHECK_FOCUS:
-                return "CHECK_FOCUS";
-            case START_INPUT_REASON_BOUND_TO_IMMS:
-                return "BOUND_TO_IMMS";
-            case START_INPUT_REASON_UNBOUND_FROM_IMMS:
-                return "UNBOUND_FROM_IMMS";
-            case START_INPUT_REASON_ACTIVATED_BY_IMMS:
-                return "ACTIVATED_BY_IMMS";
-            case START_INPUT_REASON_DEACTIVATED_BY_IMMS:
-                return "DEACTIVATED_BY_IMMS";
-            case START_INPUT_REASON_SESSION_CREATED_BY_IME:
-                return "SESSION_CREATED_BY_IME";
-            default:
-                return "Unknown=" + reason;
-        }
-    }
-
-    public static final int UNBIND_REASON_UNSPECIFIED = 0;
-    public static final int UNBIND_REASON_SWITCH_CLIENT = 1;
-    public static final int UNBIND_REASON_SWITCH_IME = 2;
-    public static final int UNBIND_REASON_DISCONNECT_IME = 3;
-    public static final int UNBIND_REASON_NO_IME = 4;
-    public static final int UNBIND_REASON_SWITCH_IME_FAILED = 5;
-    public static final int UNBIND_REASON_SWITCH_USER = 6;
-
-    @Retention(SOURCE)
-    @IntDef({UNBIND_REASON_UNSPECIFIED, UNBIND_REASON_SWITCH_CLIENT, UNBIND_REASON_SWITCH_IME,
-            UNBIND_REASON_DISCONNECT_IME, UNBIND_REASON_NO_IME, UNBIND_REASON_SWITCH_IME_FAILED,
-            UNBIND_REASON_SWITCH_USER})
-    public @interface UnbindReason {}
-
-    public static String getUnbindReason(@UnbindReason final int reason) {
-        switch (reason) {
-            case UNBIND_REASON_UNSPECIFIED:
-                return "UNSPECIFIED";
-            case UNBIND_REASON_SWITCH_CLIENT:
-                return "SWITCH_CLIENT";
-            case UNBIND_REASON_SWITCH_IME:
-                return "SWITCH_IME";
-            case UNBIND_REASON_DISCONNECT_IME:
-                return "DISCONNECT_IME";
-            case UNBIND_REASON_NO_IME:
-                return "NO_IME";
-            case UNBIND_REASON_SWITCH_IME_FAILED:
-                return "SWITCH_IME_FAILED";
-            case UNBIND_REASON_SWITCH_USER:
-                return "SWITCH_USER";
-            default:
-                return "Unknown=" + reason;
-        }
-    }
-
-    public static String softInputModeToString(@SoftInputModeFlags final int softInputMode) {
-        final StringBuilder sb = new StringBuilder();
-        final int state = softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE;
-        final int adjust = softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST;
-        final boolean isForwardNav =
-                (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0;
-
-        switch (state) {
-            case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
-                sb.append("STATE_UNSPECIFIED");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
-                sb.append("STATE_UNCHANGED");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
-                sb.append("STATE_HIDDEN");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
-                sb.append("STATE_ALWAYS_HIDDEN");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
-                sb.append("STATE_VISIBLE");
-                break;
-            case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
-                sb.append("STATE_ALWAYS_VISIBLE");
-                break;
-            default:
-                sb.append("STATE_UNKNOWN(");
-                sb.append(state);
-                sb.append(")");
-                break;
-        }
-
-        switch (adjust) {
-            case LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED:
-                sb.append("|ADJUST_UNSPECIFIED");
-                break;
-            case LayoutParams.SOFT_INPUT_ADJUST_RESIZE:
-                sb.append("|ADJUST_RESIZE");
-                break;
-            case LayoutParams.SOFT_INPUT_ADJUST_PAN:
-                sb.append("|ADJUST_PAN");
-                break;
-            case LayoutParams.SOFT_INPUT_ADJUST_NOTHING:
-                sb.append("|ADJUST_NOTHING");
-                break;
-            default:
-                sb.append("|ADJUST_UNKNOWN(");
-                sb.append(adjust);
-                sb.append(")");
-                break;
-        }
-
-        if (isForwardNav) {
-            // This is a special bit that is set by the system only during the window navigation.
-            sb.append("|IS_FORWARD_NAVIGATION");
-        }
-
-        return sb.toString();
-    }
-}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 9263b57..b799728 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -24,23 +24,21 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
-import android.graphics.drawable.Drawable;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.SparseArray;
-import android.view.DisplayListCanvas;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.RenderNodeAnimator;
@@ -71,6 +69,7 @@
     private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
 
     private static final boolean PROFILE_DRAWING = false;
+    private static final float LINE_FADE_ALPHA_MULTIPLIER = 3.5f;
     private final CellState[][] mCellStates;
 
     private final int mDotSize;
@@ -1131,8 +1130,8 @@
                     drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
                 } else {
                     if (isHardwareAccelerated() && cellState.hwAnimating) {
-                        DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
-                        displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
+                        RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+                        recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
                                 cellState.hwRadius, cellState.hwPaint);
                     } else {
                         drawCircle(canvas, (int) centerX, (int) centerY + translationY,
@@ -1172,9 +1171,9 @@
                 float centerX = getCenterXForColumn(cell.column);
                 float centerY = getCenterYForRow(cell.row);
                 if (i != 0) {
-                   // Set this line segment to slowly fade over the next second.
+                   // Set this line segment to fade away animated.
                    int lineFadeVal = (int) Math.min((elapsedRealtime -
-                           mLineFadeStart[i])/2f, 255f);
+                           mLineFadeStart[i]) * LINE_FADE_ALPHA_MULTIPLIER, 255f);
 
                     CellState state = mCellStates[cell.row][cell.column];
                     currentPath.rewind();
diff --git a/core/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java
index c029bb2..3ea873b 100644
--- a/core/java/com/google/android/collect/Lists.java
+++ b/core/java/com/google/android/collect/Lists.java
@@ -16,6 +16,7 @@
 
 package com.google.android.collect;
 
+import android.annotation.UnsupportedAppUsage;
 import java.util.ArrayList;
 import java.util.Collections;
 
@@ -33,6 +34,7 @@
      *
      * @return a newly-created, initially-empty {@code ArrayList}
      */
+    @UnsupportedAppUsage
     public static <E> ArrayList<E> newArrayList() {
         return new ArrayList<E>();
     }
diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java
index fc2c9fe..6ba3320 100644
--- a/core/java/com/google/android/collect/Maps.java
+++ b/core/java/com/google/android/collect/Maps.java
@@ -16,6 +16,7 @@
 
 package com.google.android.collect;
 
+import android.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
 
 import java.util.HashMap;
@@ -29,6 +30,7 @@
      *
      * @return a newly-created, initially-empty {@code HashMap}
      */
+    @UnsupportedAppUsage
     public static <K, V> HashMap<K, V> newHashMap() {
         return new HashMap<K, V>();
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c2ca2fc..59c29e2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -45,6 +45,7 @@
         "android_app_NativeActivity.cpp",
         "android_app_admin_SecurityLog.cpp",
         "android_opengl_EGL14.cpp",
+        "android_opengl_EGL15.cpp",
         "android_opengl_EGLExt.cpp",
         "android_opengl_GLES10.cpp",
         "android_opengl_GLES10Ext.cpp",
@@ -84,8 +85,6 @@
         "android_view_VelocityTracker.cpp",
         "android_text_AndroidCharacter.cpp",
         "android_text_Hyphenator.cpp",
-        "android_text_LineBreaker.cpp",
-        "android_text_MeasuredParagraph.cpp",
         "android_os_Debug.cpp",
         "android_os_GraphicsEnvironment.cpp",
         "android_os_HidlSupport.cpp",
@@ -161,6 +160,8 @@
         "android/graphics/pdf/PdfEditor.cpp",
         "android/graphics/pdf/PdfRenderer.cpp",
         "android/graphics/pdf/PdfUtils.cpp",
+        "android/graphics/text/LineBreaker.cpp",
+        "android/graphics/text/MeasuredText.cpp",
         "android_media_AudioRecord.cpp",
         "android_media_AudioSystem.cpp",
         "android_media_AudioTrack.cpp",
@@ -239,6 +240,7 @@
 
     shared_libs: [
         "libbpf",
+        "libnetdbpf",
         "libnetdutils",
         "libmemtrack",
         "libandroidfw",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6b55ed6..eada690 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -77,6 +77,7 @@
 extern int register_com_google_android_gles_jni_EGLImpl(JNIEnv* env);
 extern int register_com_google_android_gles_jni_GLImpl(JNIEnv* env);
 extern int register_android_opengl_jni_EGL14(JNIEnv* env);
+extern int register_android_opengl_jni_EGL15(JNIEnv* env);
 extern int register_android_opengl_jni_EGLExt(JNIEnv* env);
 extern int register_android_opengl_jni_GLES10(JNIEnv* env);
 extern int register_android_opengl_jni_GLES10Ext(JNIEnv* env);
@@ -145,6 +146,8 @@
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
+extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
 extern int register_android_view_DisplayListCanvas(JNIEnv* env);
 extern int register_android_view_TextureLayer(JNIEnv* env);
@@ -185,8 +188,6 @@
 extern int register_android_net_NetworkUtils(JNIEnv* env);
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
 extern int register_android_text_Hyphenator(JNIEnv *env);
-extern int register_android_text_MeasuredParagraph(JNIEnv* env);
-extern int register_android_text_LineBreaker(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
 extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
@@ -1336,8 +1337,6 @@
     REG_JNI(register_android_content_res_ApkAssets),
     REG_JNI(register_android_text_AndroidCharacter),
     REG_JNI(register_android_text_Hyphenator),
-    REG_JNI(register_android_text_MeasuredParagraph),
-    REG_JNI(register_android_text_LineBreaker),
     REG_JNI(register_android_view_InputDevice),
     REG_JNI(register_android_view_KeyCharacterMap),
     REG_JNI(register_android_os_Process),
@@ -1369,6 +1368,7 @@
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
     REG_JNI(register_android_opengl_jni_EGL14),
+    REG_JNI(register_android_opengl_jni_EGL15),
     REG_JNI(register_android_opengl_jni_EGLExt),
     REG_JNI(register_android_opengl_jni_GLES10),
     REG_JNI(register_android_opengl_jni_GLES10Ext),
@@ -1415,6 +1415,8 @@
     REG_JNI(register_android_graphics_pdf_PdfDocument),
     REG_JNI(register_android_graphics_pdf_PdfEditor),
     REG_JNI(register_android_graphics_pdf_PdfRenderer),
+    REG_JNI(register_android_graphics_text_MeasuredText),
+    REG_JNI(register_android_graphics_text_LineBreaker),
 
     REG_JNI(register_android_database_CursorWindow),
     REG_JNI(register_android_database_SQLiteConnection),
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 2619107..bb291e7 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -23,8 +23,6 @@
 #include <hwui/Paint.h>
 #include <utils/Log.h>
 
-#include <ResourceCache.h>
-
 #include "SkCanvas.h"
 #include "SkLatticeIter.h"
 #include "SkRegion.h"
@@ -83,12 +81,7 @@
 
     static void finalize(JNIEnv* env, jobject, jlong patchHandle) {
         int8_t* patch = reinterpret_cast<int8_t*>(patchHandle);
-        if (android::uirenderer::ResourceCache::hasInstance()) {
-            Res_png_9patch* p = (Res_png_9patch*) patch;
-            android::uirenderer::ResourceCache::getInstance().destructor(p);
-        } else {
-            delete[] patch;
-        }
+        delete[] patch;
     }
 
     static jlong getTransparentRegion(JNIEnv* env, jobject, jobject jbitmap,
diff --git a/core/jni/android_text_LineBreaker.cpp b/core/jni/android/graphics/text/LineBreaker.cpp
similarity index 96%
rename from core/jni/android_text_LineBreaker.cpp
rename to core/jni/android/graphics/text/LineBreaker.cpp
index 5439107..e1f2f2b 100644
--- a/core/jni/android_text_LineBreaker.cpp
+++ b/core/jni/android/graphics/text/LineBreaker.cpp
@@ -168,8 +168,9 @@
     {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
 };
 
-int register_android_text_LineBreaker(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods));
+int register_android_graphics_text_LineBreaker(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/text/LineBreaker", gMethods,
+                                NELEM(gMethods));
 }
 
 }
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android/graphics/text/MeasuredText.cpp
similarity index 91%
rename from core/jni/android_text_MeasuredParagraph.cpp
rename to core/jni/android/graphics/text/MeasuredText.cpp
index 18f509c..0bfadb4 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android/graphics/text/MeasuredText.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "MeasuredParagraph"
+#define LOG_TAG "MeasuredText"
 
 #include "GraphicsJNI.h"
 #include "unicode/locid.h"
@@ -83,7 +83,7 @@
 }
 
 // Regular JNI
-static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr,
+static jlong nBuildMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
                                       jcharArray javaText, jboolean computeHyphenation,
                                       jboolean computeLayout) {
     ScopedCharArrayRO text(env, javaText);
@@ -147,7 +147,7 @@
     {"nInitBuilder", "()J", (void*) nInitBuilder},
     {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
     {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
-    {"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph},
+    {"nBuildMeasuredText", "(J[CZZ)J", (void*) nBuildMeasuredText},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
 };
 
@@ -160,10 +160,10 @@
     {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt},  // Critical Native
 };
 
-int register_android_text_MeasuredParagraph(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph",
+int register_android_graphics_text_MeasuredText(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText",
             gMTMethods, NELEM(gMTMethods))
-        + RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph$Builder",
+        + RegisterMethodsOrDie(env, "android/graphics/text/MeasuredText$Builder",
             gMTBuilderMethods, NELEM(gMTBuilderMethods));
 }
 
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 9dbb8d7..b417a56 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -788,6 +788,63 @@
     return status;
 }
 
+static jobject
+android_hardware_SoundTrigger_getModelState(JNIEnv *env, jobject thiz,
+                                            jint jHandle)
+{
+    ALOGV("getModelState");
+    sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+    if (module == NULL) {
+        return NULL;
+    }
+    sp<IMemory> memory;
+    jint status = module->getModelState(jHandle, memory);
+    if (status != 0 || memory == NULL) {
+        ALOGW("getModelState, failed to get model state, status: %d", status);
+        return NULL;
+    }
+    struct sound_trigger_recognition_event* event =
+        (struct sound_trigger_recognition_event *)memory->pointer();
+    if (event == NULL) {
+        return NULL;
+    }
+    if (event->type != SOUND_MODEL_TYPE_GENERIC) {
+        ALOGW("getModelState, unsupported model type: %d", event->type);
+        return NULL;
+    }
+
+    jbyteArray jData = NULL;
+    if (event->data_size) {
+        jData = env->NewByteArray(event->data_size);
+        jbyte *nData = env->GetByteArrayElements(jData, NULL);
+        memcpy(nData, (char *)event + event->data_offset, event->data_size);
+        env->ReleaseByteArrayElements(jData, nData, 0);
+    }
+
+    jobject jAudioFormat = NULL;
+    if (event->trigger_in_data || event->capture_available) {
+        jAudioFormat = env->NewObject(gAudioFormatClass,
+                                      gAudioFormatCstor,
+                                      audioFormatFromNative(event->audio_config.format),
+                                      event->audio_config.sample_rate,
+                                      inChannelMaskFromNative(event->audio_config.channel_mask));
+
+    }
+    jobject jEvent = NULL;
+    jEvent = env->NewObject(gGenericRecognitionEventClass, gGenericRecognitionEventCstor,
+                            event->status, event->model, event->capture_available,
+                            event->capture_session, event->capture_delay_ms,
+                            event->capture_preamble_ms, event->trigger_in_data,
+                            jAudioFormat, jData);
+    if (jAudioFormat != NULL) {
+        env->DeleteLocalRef(jAudioFormat);
+    }
+    if (jData != NULL) {
+        env->DeleteLocalRef(jData);
+    }
+    return jEvent;
+}
+
 static const JNINativeMethod gMethods[] = {
     {"listModules",
         "(Ljava/util/ArrayList;)I",
@@ -817,6 +874,9 @@
     {"stopRecognition",
         "(I)I",
         (void *)android_hardware_SoundTrigger_stopRecognition},
+    {"getModelState",
+        "(I)Landroid/hardware/soundtrigger/SoundTrigger$RecognitionEvent;",
+        (void *)android_hardware_SoundTrigger_getModelState},
 };
 
 int register_android_hardware_SoundTrigger(JNIEnv *env)
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index da4cdb6..83a8c2e 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -127,10 +127,9 @@
     case AUDIO_FORMAT_DOLBY_TRUEHD:
         return ENCODING_DOLBY_TRUEHD;
     case AUDIO_FORMAT_AAC_ELD:
-            return ENCODING_AAC_ELD;
-    // FIXME needs addition of AUDIO_FORMAT_AAC_XHE
-    //case AUDIO_FORMAT_AAC_XHE:
-    //    return ENCODING_AAC_XHE;
+        return ENCODING_AAC_ELD;
+    case AUDIO_FORMAT_AAC_XHE:
+        return ENCODING_AAC_XHE;
     case AUDIO_FORMAT_AC4:
         return ENCODING_AC4;
     case AUDIO_FORMAT_E_AC3_JOC:
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 6456fe6..bf22dd2 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -29,6 +29,7 @@
 #include <media/AudioSystem.h>
 #include <media/AudioTrack.h>
 
+#include <android-base/macros.h>
 #include <binder/MemoryHeapBase.h>
 #include <binder/MemoryBase.h>
 
@@ -134,41 +135,53 @@
         callbackInfo->busy = true;
     }
 
+    // used as default argument when event callback doesn't have any, or number of
+    // frames for EVENT_CAN_WRITE_MORE_DATA
+    int arg = 0;
+    bool postEvent = false;
     switch (event) {
     // Offload only events
-    case AudioTrack::EVENT_STREAM_END:
-    case AudioTrack::EVENT_MORE_DATA:
-    // a.k.a. tear down
-    case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+    case AudioTrack::EVENT_CAN_WRITE_MORE_DATA:
+        // this event will read the info return parameter of the callback:
+        // for JNI offload, use the returned size to indicate:
+        // 1/ no data is returned through callback, as it's all done through write()
+        // 2/ do not wait as AudioTrack does when it receives 0 bytes
         if (callbackInfo->isOffload) {
-            JNIEnv *env = AndroidRuntime::getJNIEnv();
-            if (user != NULL && env != NULL) {
-                env->CallStaticVoidMethod(
-                        callbackInfo->audioTrack_class,
-                        javaAudioTrackFields.postNativeEventInJava,
-                        callbackInfo->audioTrack_ref, event, 0,0, NULL);
-                if (env->ExceptionCheck()) {
-                    env->ExceptionDescribe();
-                    env->ExceptionClear();
-                }
-            }
-        } break;
+            AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info;
+            const size_t availableForWrite = pBuffer->size;
+            arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite;
+            pBuffer->size = 0;
+        }
+        FALLTHROUGH_INTENDED;
+    case AudioTrack::EVENT_STREAM_END:
+    case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down
+        if (callbackInfo->isOffload) {
+            postEvent = true;
+        }
+        break;
 
     // PCM and offload events
     case AudioTrack::EVENT_MARKER:
-    case AudioTrack::EVENT_NEW_POS: {
+    case AudioTrack::EVENT_NEW_POS:
+        postEvent = true;
+        break;
+    default:
+        // event will not be posted
+        break;
+    }
+
+    if (postEvent) {
         JNIEnv *env = AndroidRuntime::getJNIEnv();
-        if (user != NULL && env != NULL) {
+        if (env != NULL) {
             env->CallStaticVoidMethod(
                     callbackInfo->audioTrack_class,
                     javaAudioTrackFields.postNativeEventInJava,
-                    callbackInfo->audioTrack_ref, event, 0,0, NULL);
+                    callbackInfo->audioTrack_ref, event, arg, 0, NULL);
             if (env->ExceptionCheck()) {
                 env->ExceptionDescribe();
                 env->ExceptionClear();
             }
         }
-        } break;
     }
 
     {
@@ -215,10 +228,10 @@
         jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
         jlong nativeAudioTrack, jboolean offload) {
 
-    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
-        "nativeAudioTrack=0x%" PRIX64,
+    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
+        " nativeAudioTrack=0x%" PRIX64 ", offload=%d",
         jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
-        nativeAudioTrack);
+        nativeAudioTrack, offload);
 
     sp<AudioTrack> lpTrack = 0;
 
@@ -318,7 +331,7 @@
         lpJniStorage->mCallbackData.busy = false;
 
         audio_offload_info_t offloadInfo;
-        if (offload) {
+        if (offload == JNI_TRUE) {
             offloadInfo = AUDIO_INFO_INITIALIZER;
             offloadInfo.format = format;
             offloadInfo.sample_rate = sampleRateInHertz;
@@ -331,23 +344,23 @@
         status_t status = NO_ERROR;
         switch (memoryMode) {
         case MODE_STREAM:
-
             status = lpTrack->set(
                     AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                     sampleRateInHertz,
                     format,// word length, PCM
                     nativeChannelMask,
-                    frameCount,
-                    AUDIO_OUTPUT_FLAG_NONE,
+                    offload ? 0 : frameCount,
+                    offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,
                     audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
                     0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                     0,// shared mem
                     true,// thread can call Java
                     sessionId,// audio session ID
-                    AudioTrack::TRANSFER_SYNC,
+                    offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
                     offload ? &offloadInfo : NULL,
                     -1, -1,                       // default uid, pid values
                     paa);
+
             break;
 
         case MODE_STATIC:
diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp
new file mode 100644
index 0000000..b52f137
--- /dev/null
+++ b/core/jni/android_opengl_EGL15.cpp
@@ -0,0 +1,566 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/misc.h>
+
+#include <assert.h>
+#include <EGL/egl.h>
+
+#include <ui/ANativeObjectBase.h>
+
+static int initialized = 0;
+
+// classes from EGL 1.4
+static jclass egldisplayClass;
+static jclass eglsurfaceClass;
+static jclass eglconfigClass;
+static jclass eglcontextClass;
+static jclass bufferClass;
+static jclass nioAccessClass;
+
+static jfieldID positionID;
+static jfieldID limitID;
+static jfieldID elementSizeShiftID;
+
+static jmethodID getBasePointerID;
+static jmethodID getBaseArrayID;
+static jmethodID getBaseArrayOffsetID;
+
+static jmethodID egldisplayGetHandleID;
+static jmethodID eglconfigGetHandleID;
+static jmethodID eglcontextGetHandleID;
+static jmethodID eglsurfaceGetHandleID;
+
+static jmethodID egldisplayConstructor;
+static jmethodID eglcontextConstructor;
+static jmethodID eglsurfaceConstructor;
+static jmethodID eglconfigConstructor;
+
+static jobject eglNoContextObject;
+static jobject eglNoDisplayObject;
+static jobject eglNoSurfaceObject;
+
+// classes from EGL 1.5
+static jclass eglimageClass;
+static jclass eglsyncClass;
+
+static jmethodID eglimageGetHandleID;
+static jmethodID eglsyncGetHandleID;
+
+static jmethodID eglimageConstructor;
+static jmethodID eglsyncConstructor;
+
+static jobject eglNoImageObject;
+static jobject eglNoSyncObject;
+
+/* Cache method IDs each time the class is loaded. */
+
+static void
+nativeClassInit(JNIEnv *_env, jclass glImplClass)
+{
+    // EGL 1.4 Init
+    jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig");
+    eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal);
+    jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext");
+    eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal);
+    jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay");
+    egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal);
+    jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface");
+    eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal);
+
+    eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J");
+    eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J");
+    egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J");
+    eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J");
+
+
+    eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V");
+    eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V");
+    egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V");
+    eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V");
+
+    jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT));
+    eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject);
+    jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY));
+    eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject);
+    jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE));
+    eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject);
+
+
+    jclass eglClass = _env->FindClass("android/opengl/EGL15");
+    jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;");
+    _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject);
+
+    jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;");
+    _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject);
+
+    jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;");
+    _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject);
+
+    // EGL 1.5 init
+    jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess");
+    nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal);
+
+    jclass bufferClassLocal = _env->FindClass("java/nio/Buffer");
+    bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal);
+
+    getBasePointerID = _env->GetStaticMethodID(nioAccessClass,
+            "getBasePointer", "(Ljava/nio/Buffer;)J");
+    getBaseArrayID = _env->GetStaticMethodID(nioAccessClass,
+            "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
+    getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass,
+            "getBaseArrayOffset", "(Ljava/nio/Buffer;)I");
+
+    positionID = _env->GetFieldID(bufferClass, "position", "I");
+    limitID = _env->GetFieldID(bufferClass, "limit", "I");
+    elementSizeShiftID =
+        _env->GetFieldID(bufferClass, "_elementSizeShift", "I");
+
+    jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage");
+    eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal);
+    jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync");
+    eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal);
+
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V");
+    eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V");
+
+    jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;");
+    _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject);
+
+    jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;");
+    _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject);
+}
+
+static void *
+getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset)
+{
+    jint position;
+    jint limit;
+    jint elementSizeShift;
+    jlong pointer;
+
+    position = _env->GetIntField(buffer, positionID);
+    limit = _env->GetIntField(buffer, limitID);
+    elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
+    *remaining = (limit - position) << elementSizeShift;
+    pointer = _env->CallStaticLongMethod(nioAccessClass,
+            getBasePointerID, buffer);
+    if (pointer != 0L) {
+        *array = NULL;
+        return reinterpret_cast<void*>(pointer);
+    }
+    eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J");
+    eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J");
+
+    *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass,
+            getBaseArrayID, buffer);
+    *offset = _env->CallStaticIntMethod(nioAccessClass,
+            getBaseArrayOffsetID, buffer);
+
+    return NULL;
+}
+
+static void
+releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
+{
+    _env->ReleasePrimitiveArrayCritical(array, data,
+                       commit ? 0 : JNI_ABORT);
+}
+
+static void *
+fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) {
+    if (obj == NULL){
+        jniThrowException(_env, "java/lang/IllegalArgumentException",
+                          "Object is set to null.");
+    }
+
+    jlong handle = _env->CallLongMethod(obj, mid);
+    return reinterpret_cast<void*>(handle);
+}
+
+static jobject
+toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) {
+    if (cls == eglimageClass &&
+       (EGLImage)handle == EGL_NO_IMAGE) {
+           return eglNoImageObject;
+    }
+
+    return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle));
+}
+
+// --------------------------------------------------------------------------
+/* EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateSync
+  (JNIEnv *_env, jobject _this, jobject dpy, jint type, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLSync _returnValue = (EGLSync) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglCreateSync(
+        (EGLDisplay)dpy_native,
+        (EGLenum)type,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, eglsyncClass, eglsyncConstructor, _returnValue);
+}
+
+/* EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) */
+static jboolean
+android_eglDestroySync
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync) {
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+    _returnValue = eglDestroySync(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native
+    );
+    return (jboolean)_returnValue;
+}
+
+/* EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) */
+static jint
+android_eglClientWaitSync
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags, jlong timeout) {
+    EGLint _returnValue = (EGLint) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+    _returnValue = eglClientWaitSync(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native,
+        (EGLint)flags,
+        (EGLTime)timeout
+    );
+    return (jint)_returnValue;
+}
+
+/* EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) */
+static jboolean
+android_eglGetSyncAttrib
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint attribute, jlongArray value_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+    EGLAttrib *value_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *value = (EGLAttrib *) 0;
+
+    if (!value_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "value == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(value_ref) - offset;
+    value_base = (EGLAttrib *)
+        _env->GetLongArrayElements(value_ref, (jboolean *)0);
+    value = value_base + offset;
+
+    _returnValue = eglGetSyncAttrib(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native,
+        (EGLint)attribute,
+        (EGLAttrib *)value
+    );
+
+exit:
+    if (value_base) {
+        _env->ReleaseLongArrayElements(value_ref, (jlong*)value_base,
+            _exception ? JNI_ABORT: 0);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return (jboolean)_returnValue;
+}
+
+/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglGetPlatformDisplay
+  (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLDisplay _returnValue = (EGLDisplay) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglGetPlatformDisplay(
+        (EGLenum)platform,
+        (void *)native_display,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformWindowSurface
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_window_buf, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    jarray _array = (jarray) 0;
+    jint _bufferOffset = (jint) 0;
+    EGLSurface _returnValue = (EGLSurface) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLConfig config_native = (EGLConfig) fromEGLHandle(_env, eglconfigGetHandleID, config);
+    jint _native_windowRemaining;
+    void *native_window = (void *) 0;
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _attrib_listRemaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!native_window_buf) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "native_window == null";
+        goto exit;
+    }
+    native_window = (void *)getPointer(_env, native_window_buf, (jarray*)&_array, &_native_windowRemaining, &_bufferOffset);
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _attrib_listRemaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    if (native_window == NULL) {
+        char * _native_windowBase = (char *)_env->GetPrimitiveArrayCritical(_array, (jboolean *) 0);
+        native_window = (void *) (_native_windowBase + _bufferOffset);
+    }
+    _returnValue = eglCreatePlatformWindowSurface(
+        (EGLDisplay)dpy_native,
+        (EGLConfig)config_native,
+        (void *)native_window,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_array) {
+        releasePointer(_env, _array, native_window, _exception ? JNI_FALSE : JNI_TRUE);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue);
+}
+
+/* EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreatePlatformPixmapSurface
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) {
+    jniThrowException(_env, "java/lang/UnsupportedOperationException",
+        "eglCreatePlatformPixmapSurface");
+    return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0);
+}
+
+/* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */
+static jboolean
+android_eglWaitSync
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject sync, jint flags) {
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLSync sync_native = (EGLSync) fromEGLHandle(_env, eglsyncGetHandleID, sync);
+
+    _returnValue = eglWaitSync(
+        (EGLDisplay)dpy_native,
+        (EGLSync)sync_native,
+        (EGLint)flags
+    );
+    return (jboolean)_returnValue;
+}
+
+/* EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list ) */
+static jobject
+android_eglCreateImage
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject context, jint target, jlong buffer, jlongArray attrib_list_ref, jint offset) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    EGLImage _returnValue = (EGLImage) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLContext context_native = (EGLContext) fromEGLHandle(_env, eglcontextGetHandleID, context);
+    EGLAttrib *attrib_list_base = (EGLAttrib *) 0;
+    jint _remaining;
+    EGLAttrib *attrib_list = (EGLAttrib *) 0;
+
+    if (!attrib_list_ref) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "attrib_list == null";
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "offset < 0";
+        goto exit;
+    }
+    _remaining = _env->GetArrayLength(attrib_list_ref) - offset;
+    attrib_list_base = (EGLAttrib *)
+        _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0);
+    attrib_list = attrib_list_base + offset;
+
+    _returnValue = eglCreateImage(
+        (EGLDisplay)dpy_native,
+        (EGLContext)context_native,
+        (EGLenum)target,
+        (EGLClientBuffer)buffer,
+        (EGLAttrib *)attrib_list
+    );
+
+exit:
+    if (attrib_list_base) {
+        _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base,
+            JNI_ABORT);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return toEGLHandle(_env, eglimageClass, eglimageConstructor, _returnValue);
+}
+
+/* EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image ) */
+static jboolean
+android_eglDestroyImage
+  (JNIEnv *_env, jobject _this, jobject dpy, jobject image) {
+    EGLBoolean _returnValue = (EGLBoolean) 0;
+    EGLDisplay dpy_native = (EGLDisplay) fromEGLHandle(_env, egldisplayGetHandleID, dpy);
+    EGLImage image_native = (EGLImage) fromEGLHandle(_env, eglimageGetHandleID, image);
+
+    _returnValue = eglDestroyImage(
+        (EGLDisplay)dpy_native,
+        (EGLImage)image_native
+    );
+    return (jboolean)_returnValue;
+}
+
+static const char *classPathName = "android/opengl/EGL15";
+
+static const JNINativeMethod methods[] = {
+{"_nativeClassInit", "()V", (void*)nativeClassInit },
+{"eglCreateSync", "(Landroid/opengl/EGLDisplay;I[JI)Landroid/opengl/EGLSync;", (void *) android_eglCreateSync },
+{"eglDestroySync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;)Z", (void *) android_eglDestroySync },
+{"eglClientWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;IJ)I", (void *) android_eglClientWaitSync },
+{"eglGetSyncAttrib", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I[JI)Z", (void *) android_eglGetSyncAttrib },
+{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay },
+{"eglCreatePlatformWindowSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformWindowSurface },
+{"eglCreatePlatformPixmapSurface", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLConfig;Ljava/nio/Buffer;[JI)Landroid/opengl/EGLSurface;", (void *) android_eglCreatePlatformPixmapSurface },
+{"eglWaitSync", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLSync;I)Z", (void *) android_eglWaitSync },
+{"eglCreateImage", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLContext;IJ[JI)Landroid/opengl/EGLImage;", (void *) android_eglCreateImage },
+{"eglDestroyImage", "(Landroid/opengl/EGLDisplay;Landroid/opengl/EGLImage;)Z", (void *) android_eglDestroyImage },
+};
+
+int register_android_opengl_jni_EGL15(JNIEnv *_env)
+{
+    int err;
+    err = android::AndroidRuntime::registerNativeMethods(_env, classPathName, methods, NELEM(methods));
+    return err;
+}
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b70485d..e64da5c 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -23,17 +23,25 @@
 
 namespace {
 
+int getCanLoadSystemLibraries_native() {
+    return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries();
+}
+
 void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
     ScopedUtfChars pathChars(env, path);
     android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
 }
 
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn,
+                         jobject rulesFd, jlong rulesOffset, jlong rulesLength) {
     ScopedUtfChars pathChars(env, path);
     ScopedUtfChars appNameChars(env, appName);
     ScopedUtfChars appPrefChars(env, appPref);
+
+    int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
+
     android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
-            appPrefChars.c_str(), devOptIn);
+            appPrefChars.c_str(), devOptIn, rulesFd_native, rulesOffset, rulesLength);
 }
 
 void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -51,8 +59,9 @@
 }
 
 const JNINativeMethod g_methods[] = {
+    { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) },
     { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
-    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) },
+    { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) },
     { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
     { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
 };
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index d023d22..ec98080 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -904,6 +904,21 @@
     return IPCThreadState::self()->getStrictModePolicy();
 }
 
+static jint android_os_Binder_setThreadWorkSource(jint workSource)
+{
+    return IPCThreadState::self()->setWorkSource(workSource);
+}
+
+static jint android_os_Binder_getThreadWorkSource()
+{
+    return IPCThreadState::self()->getWorkSource();
+}
+
+static jint android_os_Binder_clearThreadWorkSource()
+{
+    return IPCThreadState::self()->clearWorkSource();
+}
+
 static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
 {
     IPCThreadState::self()->flushCommands();
@@ -941,6 +956,12 @@
     { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
     { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
     { "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
+    // @CriticalNative
+    { "setThreadWorkSource", "(I)I", (void*)android_os_Binder_setThreadWorkSource },
+    // @CriticalNative
+    { "getThreadWorkSource", "()I", (void*)android_os_Binder_getThreadWorkSource },
+    // @CriticalNative
+    { "clearThreadWorkSource", "()I", (void*)android_os_Binder_clearThreadWorkSource },
     { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
     { "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
     { "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 62aa1f38..4c7defb 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -51,6 +51,8 @@
 
 static const bool kDebugPolicy = false;
 static const bool kDebugProc = false;
+// When reading `proc` files, how many bytes to read at a time
+static const int kReadSize = 4096;
 
 #if GUARD_THREAD_PRIORITY
 Mutex gKeyCreateMutex;
@@ -1034,21 +1036,35 @@
     }
     env->ReleaseStringUTFChars(file, file8);
 
-    char buffer[256];
-    const int len = read(fd, buffer, sizeof(buffer)-1);
+    std::vector<char> fileBuffer(kReadSize);
+    int numBytesRead = 0;
+    while (true) {
+        // Resize buffer to make space for contents. This might be more than we need, but once we've
+        // read we resize back down
+        fileBuffer.resize(numBytesRead + kReadSize, 0);
+        // Read in contents
+        int len = TEMP_FAILURE_RETRY(read(fd, fileBuffer.data() + numBytesRead, kReadSize));
+        numBytesRead += len;
+        if (len < 0) {
+            // If `len` is negative, an error occurred on read
+            if (kDebugProc) {
+                ALOGW("Unable to open process file: %s fd=%d\n", file8, fd);
+            }
+            close(fd);
+            return JNI_FALSE;
+        } else if (len == 0) {
+            // If nothing read, we're done
+            break;
+        }
+    }
+    // Resize back down to the amount we read
+    fileBuffer.resize(numBytesRead);
+    // Terminate buffer with null byte
+    fileBuffer.push_back('\0');
     close(fd);
 
-    if (len < 0) {
-        if (kDebugProc) {
-            ALOGW("Unable to open process file: %s fd=%d\n", file8, fd);
-        }
-        return JNI_FALSE;
-    }
-    buffer[len] = 0;
-
-    return android_os_Process_parseProcLineArray(env, clazz, buffer, 0, len,
+    return android_os_Process_parseProcLineArray(env, clazz, fileBuffer.data(), 0, numBytesRead,
             format, outStrings, outLongs, outFloats);
-
 }
 
 void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 4fdd2bc..8998cd7 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -183,7 +183,7 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-const char* const kClassPathName = "android/view/DisplayListCanvas";
+const char* const kClassPathName = "android/graphics/RecordingCanvas";
 
 static JNINativeMethod gMethods[] = {
 
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 63b0046..bb71a5d 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -576,7 +576,7 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-const char* const kClassPathName = "android/view/RenderNode";
+const char* const kClassPathName = "android/graphics/RenderNode";
 
 static const JNINativeMethod gMethods[] = {
 // ----------------------------------------------------------------------------
@@ -588,7 +588,7 @@
     { "nGetDebugSize",         "(J)I",    (void*) android_view_RenderNode_getDebugSize },
     { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
     { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
-    { "nRequestPositionUpdates",   "(JLandroid/view/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
+    { "nRequestPositionUpdates",   "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
     { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
 
 
@@ -677,7 +677,7 @@
 };
 
 int register_android_view_RenderNode(JNIEnv* env) {
-    jclass clazz = FindClassOrDie(env, "android/view/RenderNode$PositionUpdateListener");
+    jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
     gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
             "positionChanged", "(JIIII)V");
     gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 2e1e130..4eda3ab 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -166,8 +166,10 @@
     }
     Rect sourceCrop = rectFromObj(env, sourceCropObj);
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::capture(displayToken, sourceCrop, width, height,
-            useIdentityTransform, rotation, &buffer);
+    status_t res = ScreenshotClient::capture(displayToken, ui::Dataspace::V0_SRGB,
+                                             ui::PixelFormat::RGBA_8888,
+                                             sourceCrop, width, height,
+                                             useIdentityTransform, rotation, &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
@@ -195,7 +197,9 @@
     }
 
     sp<GraphicBuffer> buffer;
-    status_t res = ScreenshotClient::captureChildLayers(layerHandle, sourceCrop, frameScale, &buffer);
+    status_t res = ScreenshotClient::captureChildLayers(layerHandle, ui::Dataspace::V0_SRGB,
+                                                        ui::PixelFormat::RGBA_8888, sourceCrop,
+                                                        frameScale, &buffer);
     if (res != NO_ERROR) {
         return NULL;
     }
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index 109e65c..b3ff4db 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -32,8 +32,8 @@
 #include <utils/misc.h>
 
 #include "android-base/unique_fd.h"
-#include "bpf/BpfNetworkStats.h"
 #include "bpf/BpfUtils.h"
+#include "netdbpf/BpfNetworkStats.h"
 
 using android::bpf::hasBpfSupport;
 using android::bpf::parseBpfNetworkStatsDetail;
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
new file mode 100644
index 0000000..2797550
--- /dev/null
+++ b/core/proto/android/app/settings_enums.proto
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.app.settings;
+option java_multiple_files = true;
+
+/**
+ * The action performed in this event
+ */
+enum Action {
+    ACTION_UNKNOWN = 0;
+    PAGE_VISIBLE = 1;
+    PAGE_HIDE = 2;
+    PREF_CHANGE = 3;
+}
+
+/**
+ * Id for Settings pages. Each page must have its own unique Id.
+ */
+enum PageId {
+  // Unknown page. Should not be used in production code.
+  PAGE_UNKNOWN = 0;
+
+  // OPEN: Settings homepage
+  SETTINGS_HOMEPAGE = 1502;
+
+  // OPEN: Settings > System > Input & Gesture > Wake screen
+  SETTINGS_GESTURE_WAKE_SCREEN = 1570;
+
+  // OPEN: Settings > Network & internet > Mobile network
+  MOBILE_NETWORK = 1571;
+
+  // OPEN: Settings > Network & internet > Mobile network > Choose network
+  MOBILE_NETWORK_SELECT = 1581;
+
+  // OPEN: Settings > Network & internet > Mobile network > Mobile Data > Dialog
+  MOBILE_DATA_DIALOG = 1582;
+
+  // OPEN: Settings > Network & internet > Mobile network > Data roaming > Dialog
+  MOBILE_ROAMING_DIALOG = 1583;
+
+  // Settings > Display > Lock screen display > On lock screen
+  LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
+}
+
diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index a41edf3..1f63be9 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -512,7 +512,9 @@
         optional int32  vts_coverage = 43;
         optional string zygote = 44;
 
-        // Next Tag: 45
+        optional string gfx_driver_whitelist_0 = 45;
+
+        // Next Tag: 46
     }
     optional Ro ro = 21;
 
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a02602e..47dbc07 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -397,9 +397,10 @@
         // Ordered GPU debug layer list
         // i.e. <layer1>:<layer2>:...:<layerN>
         optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-
         // App will load ANGLE instead of native GLES drivers.
         optional SettingProto angle_enabled_app = 3;
+        // App that can provide layer libraries.
+        optional SettingProto debug_layer_app = 4;
     }
     optional Gpu gpu = 59;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1ae5f03..374c7ea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4342,7 +4342,7 @@
         </activity>
 
         <activity android:name="com.android.internal.app.NetInitiatedActivity"
-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+                android:theme="@style/Theme.Dialog.Confirmation"
                 android:excludeFromRecents="true"
                 android:process=":ui">
         </activity>
@@ -4363,7 +4363,7 @@
         <activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
                 android:excludeFromRecents="true"
                 android:process=":ui"
-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert">
+                android:theme="@style/Theme.Dialog.Confirmation">
             <intent-filter android:priority="1000">
                 <action android:name="android.os.action.CREATE_USER" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -4371,24 +4371,24 @@
         </activity>
 
         <activity android:name="com.android.internal.app.SuspendedAppActivity"
-                  android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+                  android:theme="@style/Theme.Dialog.Confirmation"
                   android:excludeFromRecents="true"
                   android:process=":ui">
         </activity>
 
         <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+                android:theme="@style/Theme.Dialog.Confirmation"
                 android:excludeFromRecents="true"
                 android:process=":ui">
         </activity>
 
         <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
-                  android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+                  android:theme="@style/Theme.Dialog.Confirmation"
                   android:excludeFromRecents="true">
         </activity>
 
         <activity android:name="com.android.internal.app.HarmfulAppWarningActivity"
-                  android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+                  android:theme="@style/Theme.Dialog.Confirmation"
                   android:excludeFromRecents="true"
                   android:process=":ui"
                   android:label="@string/harmful_app_warning_title"
diff --git a/core/res/res/layout/harmful_app_warning_dialog.xml b/core/res/res/layout/harmful_app_warning_dialog.xml
index d41691f..62ca7a6 100644
--- a/core/res/res/layout/harmful_app_warning_dialog.xml
+++ b/core/res/res/layout/harmful_app_warning_dialog.xml
@@ -49,7 +49,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:gravity="center_vertical"
-                    android:textColor="@color/primary_text_material_light"
+                    android:textColor="?attr/textColorPrimary"
                     android:textSize="@dimen/text_size_subhead_material"
                     android:paddingLeft="@dimen/harmful_app_icon_name_padding">
             </TextView>
@@ -65,4 +65,4 @@
                 android:lineSpacingMultiplier="@dimen/harmful_app_message_line_spacing_modifier"
                 android:textSize="@dimen/text_size_body_1_material"/>
     </LinearLayout>
-</ScrollView>
\ No newline at end of file
+</ScrollView>
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 48b59c7..176e89e 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -15,6 +15,7 @@
 -->
 <resources>
     <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+    <style name="Theme.Dialog.Confirmation" parent="Theme.Leanback.Dialog.Confirmation" />
     <style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
     <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cdb65ed..a25c998 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1141,6 +1141,15 @@
 
         <!-- Alpha value of the spot shadow projected by elevated views, between 0 and 1. -->
         <attr name="spotShadowAlpha" format="float" />
+
+        <!-- <p>Whether or not the force dark feature is allowed to be applied to this theme.
+             <p>Setting this to false will disable the auto-dark feature on everything this
+             theme is applied to along with anything drawn by any children of views using
+             this theme.
+             <p>Setting this to true will allow this view to be automatically made dark, however
+             a value of 'true' will not override any 'false' value in its parent chain nor will
+             it prevent any 'false' in any of its children. -->
+        <attr name="forceDarkAllowed" format="boolean" />
     </declare-styleable>
 
     <!-- **************************************************************** -->
@@ -3116,8 +3125,13 @@
              {@link android.R.attr#ambientShadowAlpha} theme attribute. -->
         <attr name="outlineAmbientShadowColor" format="color" />
 
-        <!-- Whether to allow the rendering system to force this View to render as light-on-dark. -->
-        <attr name="allowForceDark" format="boolean" />
+        <!-- <p>Whether or not the force dark feature is allowed to be applied to this View.
+             <p>Setting this to false will disable the auto-dark feature on this View draws
+             including any descendants.
+             <p>Setting this to true will allow this view to be automatically made dark, however
+             a value of 'true' will not override any 'false' value in its parent chain nor will
+             it prevent any 'false' in any of its children. -->
+        <attr name="forceDarkAllowed" format="boolean" />
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -4551,6 +4565,11 @@
         <attr name="typeface" />
         <!-- Font family (named by string or as a font resource reference) for the text. -->
         <attr name="fontFamily" />
+        <!-- Specifies the {@link android.os.LocaleList} for the text.
+             May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
+             When not specified or an empty string is given, it will fallback to the default one.
+             {@see android.os.LocaleList#forLanguageTags(String)} -->
+        <attr name="textLocale" format="string" />
         <!-- Color of the text selection highlight. -->
         <attr name="textColorHighlight" />
         <!-- Color of the hint text. -->
@@ -4642,6 +4661,13 @@
         <attr name="textFontWeight" />
         <!-- Font family (named by string or as a font resource reference) for the text. -->
         <attr name="fontFamily" />
+        <!-- Specifies the {@link android.os.LocaleList} for the text in this TextView.
+             If not given, the system default will be used.
+             May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
+             When not specified or an empty string is given, it will fallback to the default one.
+             {@see android.os.LocaleList#forLanguageTags(String)}
+             {@see android.text.TextView#setTextLocales(android.os.LocaleList)} -->
+        <attr name="textLocale" format="string" />
         <!-- Text color for links. -->
         <attr name="textColorLink" />
         <!-- Makes the cursor visible (the default) or invisible. -->
@@ -7882,6 +7908,9 @@
         <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
         <attr name="supportsAmbientMode" format="boolean" />
 
+        <!-- Uri that specifies a settings Slice for this wallpaper. -->
+        <attr name="settingsSliceUri" />
+
     </declare-styleable>
 
     <!-- Use <code>dream</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cb97a2a..e257a5c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -378,8 +378,8 @@
                    Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
                [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
                    use the following format to specify static IP configuration:
-		       ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
-                       domains=<comma-sep-domains> 
+                       ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
+                       domains=<comma-sep-domains>
          -->
     <string-array translatable="false" name="config_ethernet_interfaces">
         <!--
@@ -697,6 +697,10 @@
     <!-- Wifi driver supports IEEE80211AC for softap -->
     <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
 
+    <!-- Indicates that local-only hotspot should be brought up at 5GHz.  This option is
+         for automotive builds only (the one that have PackageManager#FEATURE_AUTOMOTIVE) -->
+    <bool translatable="false" name="config_wifi_local_only_hotspot_5ghz">false</bool>
+
     <!-- Flag indicating whether we should enable the automatic brightness.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
@@ -3553,4 +3557,8 @@
 
     <!-- Whether or not the "SMS app service" feature is enabled -->
     <bool name="config_useSmsAppService">true</bool>
+
+    <!-- Component name for default assistant on this device -->
+    <string name="config_defaultAssistantComponentName">#+UNSET</string>
+
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9551718..b790829 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2908,13 +2908,15 @@
         <public name="opticalInsetTop" />
         <public name="opticalInsetRight" />
         <public name="opticalInsetBottom" />
-        <public name="allowForceDark" />
+        <public name="forceDarkAllowed" />
         <public name="supportsAmbientMode" />
         <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
         <public name="usesNonSdkApi" />
         <public name="minimumUiTimeout" />
         <public name="isLightTheme" />
         <public name="isSplitRequired" />
+        <public name="textLocale" />
+        <public name="settingsSliceUri" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fa44061..9ea82a9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2924,55 +2924,55 @@
     <!-- Title for EditText context menu [CHAR LIMIT=20] -->
     <string name="editTextMenuTitle">Text actions</string>
 
-    <!-- Label for item in the text selection menu to trigger an Email app. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to trigger an Email app. Should be a verb. [CHAR LIMIT=30] -->
     <string name="email">Email</string>
 
     <!-- Accessibility description for an item in the text selection menu to trigger an Email app [CHAR LIMIT=NONE] -->
     <string name="email_desc">Email selected address</string>
 
-    <!-- Label for item in the text selection menu to trigger a Dialer app. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to trigger a Dialer app. Should be a verb. [CHAR LIMIT=30] -->
     <string name="dial">Call</string>
 
     <!-- Accessibility description for an item in the text selection menu to call a phone number [CHAR LIMIT=NONE] -->
     <string name="dial_desc">Call selected phone number</string>
 
-    <!-- Label for item in the text selection menu to trigger a Map app. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to trigger a Map app. Should be a verb. [CHAR LIMIT=30] -->
     <string name="map">Map</string>
 
     <!-- Accessibility description for an item in the text selection menu to open maps for an address [CHAR LIMIT=NONE] -->
     <string name="map_desc">Locate selected address</string>
 
-    <!-- Label for item in the text selection menu to trigger a Browser app. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to trigger a Browser app. Should be a verb. [CHAR LIMIT=30] -->
     <string name="browse">Open</string>
 
     <!-- Accessibility description for an item in the text selection menu to open a URL in a browser [CHAR LIMIT=NONE] -->
     <string name="browse_desc">Open selected URL</string>
 
-    <!-- Label for item in the text selection menu to trigger an SMS app. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to trigger an SMS app. Should be a verb. [CHAR LIMIT=30] -->
     <string name="sms">Message</string>
 
     <!-- Accessibility description for an item in the text selection menu to send an SMS to a phone number [CHAR LIMIT=NONE] -->
     <string name="sms_desc">Message selected phone number</string>
 
-    <!-- Label for item in the text selection menu to trigger adding a contact. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to trigger adding a contact. Should be a verb. [CHAR LIMIT=30] -->
     <string name="add_contact">Add</string>
 
     <!-- Accessibility description for an item in the text selection menu to add the selected detail to contacts [CHAR LIMIT=NONE] -->
     <string name="add_contact_desc">Add to contacts</string>
 
-    <!-- Label for item in the text selection menu to view the calendar for the selected time/date. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to view the calendar for the selected time/date. Should be a verb. [CHAR LIMIT=30] -->
     <string name="view_calendar">View</string>
 
     <!-- Accessibility description for an item in the text selection menu to view the calendar for a date [CHAR LIMIT=NONE]-->
     <string name="view_calendar_desc">View selected time in calendar</string>
 
-    <!-- Label for item in the text selection menu to create a calendar event at the selected time/date. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to create a calendar event at the selected time/date. Should be a verb. [CHAR LIMIT=30] -->
     <string name="add_calendar_event">Schedule</string>
 
     <!-- Accessibility description for an item in the text selection menu to schedule an event for a date [CHAR LIMIT=NONE] -->
     <string name="add_calendar_event_desc">Schedule event for selected time</string>
 
-    <!-- Label for item in the text selection menu to track a selected flight number. Should be a verb. [CHAR LIMIT=20] -->
+    <!-- Label for item in the text selection menu to track a selected flight number. Should be a verb. [CHAR LIMIT=30] -->
     <string name="view_flight">Track</string>
 
     <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 09da4fc..81a1bf8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1875,6 +1875,7 @@
   <java-symbol type="bool" name="config_wifi_background_scan_support" />
   <java-symbol type="bool" name="config_wifi_dual_band_support" />
   <java-symbol type="bool" name="config_wifi_convert_apband_5ghz_to_any" />
+  <java-symbol type="bool" name="config_wifi_local_only_hotspot_5ghz" />
   <java-symbol type="bool" name="config_wifi_fast_bss_transition_enabled" />
   <java-symbol type="bool" name="config_wimaxEnabled" />
   <java-symbol type="bool" name="show_ongoing_ime_switcher" />
@@ -3480,4 +3481,6 @@
   <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
 
   <java-symbol type="bool" name="config_useSmsAppService" />
+
+  <java-symbol type="string" name="config_defaultAssistantComponentName" />
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index a7530ce..ad38f3d 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -880,6 +880,9 @@
         <item name="windowActivityTransitions">false</item>
     </style>
 
+    <!-- @hide Special theme for the default system Activity-based Alert dialogs. -->
+    <style name="Theme.Dialog.Confirmation" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
+
     <!-- Theme for a window that looks like a toast. -->
     <style name="Theme.Toast" parent="Theme.DeviceDefault.Dialog">
         <item name="windowBackground">?attr/toastFrameBackground</item>
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index f71df9f..a80725c 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -130,4 +130,7 @@
         <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
       </style>
 
+    <!-- @hide Special theme for the default system Activity-based Alert dialogs. -->
+    <style name="Theme.Leanback.Dialog.Confirmation" parent="Theme.DeviceDefault.Dialog.Alert" />
+
 </resources>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 307e2e8..041fb7e 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -47,7 +47,6 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
-    conscrypt \
     telephony-common \
     org.apache.http.legacy \
     android.test.base \
diff --git a/core/tests/coretests/assets/fonts/1em_bidi_font.ttf b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf
new file mode 100644
index 0000000..4599254
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/1em_bidi_font.ttf
Binary files differ
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 6256d08..0036186 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -13,31 +13,149 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.content;
 
-import android.content.ContentResolver;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
-@Suppress  // Failing.
-public class ContentResolverTest extends AndroidTestCase {
-    private ContentResolver mContentResolver;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ImageDecoder;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Size;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContentResolver = mContext.getContentResolver();
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentResolverTest {
+
+    private ContentResolver mResolver;
+    private IContentProvider mProvider;
+    private ContentProviderClient mClient;
+
+    private int mSize = 256_000;
+    private MemoryFile mImage;
+
+    @Before
+    public void setUp() throws Exception {
+        mResolver = InstrumentationRegistry.getInstrumentation().getTargetContext()
+                .getContentResolver();
+        mProvider = mock(IContentProvider.class);
+        mClient = new ContentProviderClient(mResolver, mProvider, false);
+
+        mImage = new MemoryFile("temp.png", mSize);
     }
 
-    @LargeTest
-    public void testCursorFinalizer() throws Exception {
-        // TODO: Want a test case that more predictably reproduce this issue. Selected
-        // 600 as this causes the problem 100% of the runs on current hw, it might not
-        // do so on some other configuration though.
-        for (int i = 0; i < 600; i++) {
-            mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
-        }
+    @After
+    public void tearDown() throws Exception {
+        mImage.close();
+        mImage = null;
+    }
+
+    private void initImage(int width, int height) throws Exception {
+        final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bitmap);
+
+        canvas.drawColor(Color.RED);
+
+        final Paint paint = new Paint();
+        paint.setColor(Color.BLUE);
+        paint.setStyle(Paint.Style.FILL);
+        canvas.drawRect(0, 0, width / 2, height / 2, paint);
+
+        bitmap.compress(Bitmap.CompressFormat.PNG, 90, mImage.getOutputStream());
+
+        final AssetFileDescriptor afd = new AssetFileDescriptor(
+                new ParcelFileDescriptor(mImage.getFileDescriptor()), 0, mSize, null);
+        when(mProvider.openTypedAssetFile(any(), any(), any(), any(), any())).thenReturn(afd);
+    }
+
+    private static void assertImageAspectAndContents(Bitmap bitmap) {
+        // And correct aspect ratio
+        final int before = (100 * 1280) / 960;
+        final int after = (100 * bitmap.getWidth()) / bitmap.getHeight();
+        assertEquals(before, after);
+
+        // And scaled correctly
+        final int halfX = bitmap.getWidth() / 2;
+        final int halfY = bitmap.getHeight() / 2;
+        assertEquals(Color.BLUE, bitmap.getPixel(halfX - 10, halfY - 10));
+        assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY - 10));
+        assertEquals(Color.RED, bitmap.getPixel(halfX - 10, halfY + 10));
+        assertEquals(Color.RED, bitmap.getPixel(halfX + 10, halfY + 10));
+    }
+
+    @Test
+    public void testLoadThumbnail_Normal() throws Exception {
+        initImage(1280, 960);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(1280, 960), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be untouched
+        assertEquals(1280, res.getWidth());
+        assertEquals(960, res.getHeight());
+
+        assertImageAspectAndContents(res);
+    }
+
+    @Test
+    public void testLoadThumbnail_Scaling() throws Exception {
+        initImage(1280, 960);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(320, 240), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be much smaller
+        assertTrue(res.getWidth() <= 640);
+        assertTrue(res.getHeight() <= 480);
+
+        assertImageAspectAndContents(res);
+    }
+
+    @Test
+    public void testLoadThumbnail_Aspect() throws Exception {
+        initImage(1280, 960);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(240, 320), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be much smaller
+        assertTrue(res.getWidth() <= 640);
+        assertTrue(res.getHeight() <= 480);
+
+        assertImageAspectAndContents(res);
+    }
+
+    @Test
+    public void testLoadThumbnail_Tiny() throws Exception {
+        initImage(32, 24);
+
+        Bitmap res = ContentResolver.loadThumbnail(mClient,
+                Uri.parse("content://com.example/"), new Size(320, 240), null,
+                ImageDecoder.ALLOCATOR_SOFTWARE);
+
+        // Size should be untouched
+        assertEquals(32, res.getWidth());
+        assertEquals(24, res.getHeight());
+
+        assertImageAspectAndContents(res);
     }
 }
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
new file mode 100644
index 0000000..c8a3098
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityThread;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ContextTest {
+    @Test
+    public void testDisplayIdForSystemContext() {
+        final Context systemContext =
+                ActivityThread.currentActivityThread().getSystemContext();
+
+        assertEquals(systemContext.getDisplay().getDisplayId(), systemContext.getDisplayId());
+    }
+
+    @Test
+    public void testDisplayIdForTestContext() {
+        final Context testContext =
+                InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        assertEquals(testContext.getDisplay().getDisplayId(), testContext.getDisplayId());
+    }
+
+    @Test
+    public void testDisplayIdForDefaultDisplayContext() {
+        final Context testContext =
+                InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final WindowManager wm = testContext.getSystemService(WindowManager.class);
+        final Context defaultDisplayContext =
+                testContext.createDisplayContext(wm.getDefaultDisplay());
+
+        assertEquals(defaultDisplayContext.getDisplay().getDisplayId(),
+                defaultDisplayContext.getDisplayId());
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
new file mode 100644
index 0000000..1beb598
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class BinderTest extends TestCase {
+
+    @SmallTest
+    public void testSetWorkSource() throws Exception {
+        Binder.setThreadWorkSource(100);
+        assertEquals(100, Binder.getThreadWorkSource());
+    }
+
+    @SmallTest
+    public void testClearWorkSource() throws Exception {
+        Binder.setThreadWorkSource(100);
+        Binder.clearThreadWorkSource();
+        assertEquals(-1, Binder.getThreadWorkSource());
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 80281b6..6966448 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -41,6 +41,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.os.FileUtils.MemoryPipe;
@@ -510,6 +511,20 @@
                 MODE_WRITE_ONLY | MODE_CREATE | MODE_APPEND);
     }
 
+    @Test
+    public void testTranslateMode_Invalid() throws Exception {
+        try {
+            translateModeStringToPosix("rwx");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            translateModeStringToPosix("");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     private static void assertTranslate(String string, int posix, int pfd) {
         assertEquals(posix, translateModeStringToPosix(string));
         assertEquals(string, translateModePosixToString(posix));
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 60abd94..9778acb 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -368,6 +368,8 @@
                     Settings.Global.PRIV_APP_OOB_ENABLED,
                     Settings.Global.PRIV_APP_OOB_LIST,
                     Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
+                    Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED,
+                    Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED,
                     Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS,
                     Settings.Global.RADIO_BLUETOOTH,
                     Settings.Global.RADIO_CELL,
@@ -450,6 +452,7 @@
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.ANGLE_ENABLED_APP,
+                    Settings.Global.GPU_DEBUG_LAYER_APP,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
                     Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
diff --git a/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
new file mode 100644
index 0000000..1208d7c
--- /dev/null
+++ b/core/tests/coretests/src/android/text/LayoutBidiCursorPathTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.content.Context;
+import android.graphics.Path;
+import android.graphics.Typeface;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.method.MetaKeyKeyListener;
+import android.view.KeyEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LayoutBidiCursorPathTest {
+
+    private static final float BIDI_TEXT_SIZE = 12f;
+    private static final String LTR_TEXT = "hello";
+    private static final String RTL_TEXT = "مرحبا";
+
+    private SpannableStringBuilder mBidiText;
+    private TextPaint mTextPaint;
+
+    @Before
+    public void setup() {
+        mBidiText = new SpannableStringBuilder(LTR_TEXT + RTL_TEXT);
+
+        final Context context = InstrumentationRegistry.getTargetContext();
+        mTextPaint = new TextPaint();
+        mTextPaint.setTypeface(
+                Typeface.createFromAsset(context.getAssets(), "fonts/1em_bidi_font.ttf"));
+        mTextPaint.setTextSize(BIDI_TEXT_SIZE);
+    }
+
+    @Test
+    public void testGetCursorPathSegments() {
+        // Setup layout and Act.
+        final Path actualPath = new Path();
+        setupLayoutAndGetCursorPath(actualPath);
+
+        // Expected path.
+        final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f;
+        final int top = 0;
+        // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here.
+        final int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f);
+
+        final Path expectedPath = new Path();
+
+        expectedPath.moveTo(h1, top);
+        expectedPath.lineTo(h1, bottom);
+
+        // Assert.
+        assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f);
+    }
+
+    @Test
+    public void testGetCursorPath_whenShiftIsPressed() {
+        // When shift is pressed a triangle is drawn at the bottom quarter of the cursor.
+        // Set up key.
+        final MetaKeyKeyListener metaKeyKeyListener = new MetaKeyKeyListener() {};
+        metaKeyKeyListener
+            .onKeyDown(null /*view*/, mBidiText, KeyEvent.KEYCODE_SHIFT_RIGHT, null /*keyEvent*/);
+
+        // Setup layout and Act.
+        final Path actualPath = new Path();
+        setupLayoutAndGetCursorPath(actualPath);
+
+        // Expected path.
+        final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f;
+        final int top = 0;
+        // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here.
+        int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f);
+        // Draw a triangle at the bottom quarter of the cursor, thus cut the cursor to its 3/4
+        // length.
+        final int dist = (bottom - top) / 4;
+        bottom -= dist;
+
+        final Path expectedPath = new Path();
+
+        expectedPath.moveTo(h1, top);
+        expectedPath.lineTo(h1, bottom);
+
+        expectedPath.moveTo(h1, bottom);
+        expectedPath.lineTo(h1 - dist, bottom + dist);
+
+        expectedPath.moveTo(h1 - dist, bottom + dist - 0.5f);
+        expectedPath.lineTo(h1 + dist, bottom + dist - 0.5f);
+
+        expectedPath.moveTo(h1 + dist, bottom + dist);
+        expectedPath.lineTo(h1, bottom);
+
+        // Assert.
+        assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f);
+    }
+
+    @Test
+    public void testGetCursorPath_whenAltIsPressed() {
+        // When alt is pressed a triangle is drawn at the top quarter of the cursor.
+        // Set up key.
+        final MetaKeyKeyListener metaKeyKeyListener = new MetaKeyKeyListener() {};
+        metaKeyKeyListener
+            .onKeyDown(null /*view*/, mBidiText, KeyEvent.KEYCODE_ALT_RIGHT, null /*keyEvent*/);
+
+        // Setup layout and Act.
+        final Path actualPath = new Path();
+        setupLayoutAndGetCursorPath(actualPath);
+
+        // Expected path.
+        final float h1 = BIDI_TEXT_SIZE * LTR_TEXT.length() - 0.5f;
+        int top = 0;
+        // sTypoLineGap is set to 1/5 of the Height in font metrics of the font file used here.
+        final int bottom = Math.round(BIDI_TEXT_SIZE + BIDI_TEXT_SIZE / 5f);
+        // Draw a triangle at the top quarter of the cursor, thus cut the cursor to its 3/4 length.
+        final int dist = (bottom - top) / 4;
+        top += dist;
+
+        final Path expectedPath = new Path();
+
+        expectedPath.moveTo(h1, top);
+        expectedPath.lineTo(h1, bottom);
+
+        expectedPath.moveTo(h1, top);
+        expectedPath.lineTo(h1 - dist, top - dist);
+
+        expectedPath.moveTo(h1 - dist, top - dist + 0.5f);
+        expectedPath.lineTo(h1 + dist, top - dist + 0.5f);
+
+        expectedPath.moveTo(h1 + dist, top - dist);
+        expectedPath.lineTo(h1, top);
+
+        // Assert.
+        assertArrayEquals(expectedPath.approximate(0f), actualPath.approximate(0f), 0f);
+    }
+
+    private void setupLayoutAndGetCursorPath(Path path) {
+        final Layout layout = StaticLayout.Builder.obtain(
+                mBidiText, 0, mBidiText.length(),  mTextPaint, Integer.MAX_VALUE)
+                .setIncludePad(false)
+                .build();
+
+        layout.getCursorPath(LTR_TEXT.length(), path, mBidiText);
+    }
+}
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index f3d6013..3d15eb9 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -73,7 +73,7 @@
         assertEquals(0, mt.getWidths().size());
         assertEquals(0, mt.getSpanEndCache().size());
         assertEquals(0, mt.getFontMetrics().size());
-        assertNull(mt.getNativeMeasuredParagraph());
+        assertNull(mt.getMeasuredText());
 
         // Recycle it
         MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
@@ -85,7 +85,7 @@
         assertEquals(0, mt2.getWidths().size());
         assertEquals(0, mt2.getSpanEndCache().size());
         assertEquals(0, mt2.getFontMetrics().size());
-        assertNull(mt.getNativeMeasuredParagraph());
+        assertNull(mt.getMeasuredText());
 
         mt2.recycle();
     }
@@ -107,7 +107,7 @@
         assertEquals(10, mt.getWidths().get(2), 0);
         assertEquals(0, mt.getSpanEndCache().size());
         assertEquals(0, mt.getFontMetrics().size());
-        assertNull(mt.getNativeMeasuredParagraph());
+        assertNull(mt.getMeasuredText());
 
         // Recycle it
         MeasuredParagraph mt2 =
@@ -124,7 +124,7 @@
         assertEquals(5, mt2.getWidths().get(2), 0);
         assertEquals(0, mt2.getSpanEndCache().size());
         assertEquals(0, mt2.getFontMetrics().size());
-        assertNull(mt.getNativeMeasuredParagraph());
+        assertNull(mt.getMeasuredText());
 
         mt2.recycle();
     }
@@ -144,7 +144,7 @@
         assertEquals(1, mt.getSpanEndCache().size());
         assertEquals(3, mt.getSpanEndCache().get(0));
         assertNotEquals(0, mt.getFontMetrics().size());
-        assertNotNull(mt.getNativeMeasuredParagraph());
+        assertNotNull(mt.getMeasuredText());
 
         // Recycle it
         MeasuredParagraph mt2 =
@@ -159,7 +159,7 @@
         assertEquals(1, mt2.getSpanEndCache().size());
         assertEquals(4, mt2.getSpanEndCache().get(0));
         assertNotEquals(0, mt2.getFontMetrics().size());
-        assertNotNull(mt.getNativeMeasuredParagraph());
+        assertNotNull(mt.getMeasuredText());
 
         mt2.recycle();
     }
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index c98e646..45a5010 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -20,7 +20,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.Suppress;
 import android.text.InputType;
 import android.util.KeyUtils;
 import android.view.KeyEvent;
@@ -239,7 +238,6 @@
     }
 
     @Test
-    @Suppress
     public void testEmojiModifier() {
         EditorState state = new EditorState();
 
@@ -256,20 +254,15 @@
         // Isolated multiple emoji modifier
         state.setByString("| U+1F3FB U+1F3FB");
         forwardDelete(state, 0);
-        state.assertEquals("| U+1F3FB");
-        forwardDelete(state, 0);
         state.assertEquals("|");
 
         // Multiple emoji modifiers
         state.setByString("| U+1F466 U+1F3FB U+1F3FB");
         forwardDelete(state, 0);
-        state.assertEquals("| U+1F3FB");
-        forwardDelete(state, 0);
         state.assertEquals("|");
     }
 
     @Test
-    @Suppress
     public void testMixedEdgeCases() {
         EditorState state = new EditorState();
 
@@ -318,7 +311,7 @@
         // COMBINING ENCLOSING KEYCAP + emoji modifier
         state.setByString("| '1' U+20E3 U+1F3FB");
         forwardDelete(state, 0);
-        state.assertEquals("| U+1F3FB");
+        state.assertEquals("|");
 
         // Emoji modifier + COMBINING ENCLOSING KEYCAP
         state.setByString("| U+1F466 U+1F3FB U+20E3");
@@ -360,7 +353,7 @@
         // Variation selector + emoji modifier
         state.setByString("| U+2665 U+FE0F U+1F3FB");
         forwardDelete(state, 0);
-        state.assertEquals("| U+1F3FB");
+        state.assertEquals("|");
 
         // Emoji modifier + variation selector
         state.setByString("| U+1F466 U+1F3FB U+FE0F");
@@ -396,7 +389,7 @@
         // Start with ZERO WIDTH JOINER + emoji modifier
         state.setByString("| U+200D U+1F3FB");
         forwardDelete(state, 0);
-        state.assertEquals("| U+1F3FB");
+        state.assertEquals("|");
 
         // ZERO WIDTH JOINER + emoji modifier
         state.setByString("| U+1F469 U+200D U+1F3FB");
@@ -409,17 +402,14 @@
         state.assertEquals("|");
 
         // Emoji modifier + ZERO WIDTH JOINER
-        state.setByString("| U+1F466 U+1F3FB U+200D U+1F469");
-        forwardDelete(state, 0);
-        state.assertEquals("| U+1F469");
-        forwardDelete(state, 0);
-        state.assertEquals("|");
+        // TODO(nona): Revive this test once HarfBuzz is updated to 2.0.2 (b/117953171)
+        // state.setByString("| U+1F466 U+1F3FB U+200D U+1F469");
+        // forwardDelete(state, 0);
+        // state.assertEquals("|");
 
         // Regional indicator symbol + emoji modifier
         state.setByString("| U+1F1FA U+1F3FB");
         forwardDelete(state, 0);
-        state.assertEquals("| U+1F3FB");
-        forwardDelete(state, 0);
         state.assertEquals("|");
 
         // Emoji modifier + regional indicator symbol
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 1a480c7..023526f 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -54,6 +54,13 @@
         MotionEvent motionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN,
                 pointerCount, properties, coords,
                 0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0);
+
+        MotionEvent motionEvent_Single = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+                ACTION_DOWN /* action */, 0f /* x */, 0f /* y */, 0/* pressure */, 0 /* size */,
+                0 /* metaState */, 0 /* xPrecision */, 0 /* yPrecision */,
+                0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_TOUCHSCREEN, displayId);
+
+        assertEquals(displayId, motionEvent_Single.getDisplayId());
         assertEquals(displayId, motionEvent.getDisplayId());
 
         displayId = 5;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index e891fc9..8646c68 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -307,6 +307,24 @@
     }
 
     @Test
+    public void testDetectLanguage() {
+        if (isTextClassifierDisabled()) return;
+        String text = "This is English text";
+        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+        TextLanguage textLanguage = mClassifier.detectLanguage(request);
+        assertThat(textLanguage, isTextLanguage("en"));
+    }
+
+    @Test
+    public void testDetectLanguage_japanese() {
+        if (isTextClassifierDisabled()) return;
+        String text = "これは日本語のテキストです";
+        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
+        TextLanguage textLanguage = mClassifier.detectLanguage(request);
+        assertThat(textLanguage, isTextLanguage("ja"));
+    }
+
+    @Test
     public void testSetTextClassifier() {
         TextClassifier classifier = mock(TextClassifier.class);
         mTcm.setTextClassifier(classifier);
@@ -444,4 +462,23 @@
             }
         };
     }
+
+    private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
+        return new BaseMatcher<TextLanguage>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextLanguage) {
+                    TextLanguage result = (TextLanguage) o;
+                    return result.getLocaleHypothesisCount() > 0
+                            && languageTag.equals(result.getLocale(0).toLanguageTag());
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("locale=").appendValue(languageTag);
+            }
+        };
+    }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
new file mode 100644
index 0000000..75ca769
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+
+import android.icu.util.ULocale;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for TextLanguage.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class TextLanguageTest {
+
+    private static final float EPSILON = 0.000001f;
+
+    @Test
+    public void testParcel() throws Exception {
+        final String bundleKey = "experiment.int";
+        final Bundle bundle = new Bundle();
+        bundle.putInt(bundleKey, 1234);
+
+        final TextLanguage reference = new TextLanguage.Builder()
+                .setId("id")
+                .setExtras(bundle)
+                .putLocale(ULocale.ENGLISH, 0.8f)
+                .putLocale(ULocale.GERMAN, 0.2f)
+                .build();
+
+        final Parcel parcel = Parcel.obtain();
+        reference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        final TextLanguage result = TextLanguage.CREATOR.createFromParcel(parcel);
+
+        assertEquals("id", result.getId());
+        assertEquals(1234, result.getExtras().getInt(bundleKey));
+        assertEquals(2, result.getLocaleHypothesisCount());
+        assertEquals(ULocale.ENGLISH, result.getLocale(0));
+        assertEquals(0.8f, result.getConfidenceScore(ULocale.ENGLISH), EPSILON);
+        assertEquals(ULocale.GERMAN, result.getLocale(1));
+        assertEquals(0.2f, result.getConfidenceScore(ULocale.GERMAN), EPSILON);
+    }
+
+    @Test
+    public void testRequestParcel() throws Exception {
+        final String text = "This is random text";
+        final String bundleKey = "experiment.str";
+        final Bundle bundle = new Bundle();
+        bundle.putString(bundleKey, "bundle");
+
+        final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
+                .setExtras(bundle)
+                .build();
+
+        final Parcel parcel = Parcel.obtain();
+        reference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        final TextLanguage.Request result = TextLanguage.Request.CREATOR.createFromParcel(parcel);
+
+        assertEquals(text, result.getText());
+        assertEquals("bundle", result.getExtras().getString(bundleKey));
+    }
+}
diff --git a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
index 1097bc7..01382aa 100644
--- a/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
+++ b/core/tests/packagemanagertests/src/android/content/pm/KernelPackageMappingTests.java
@@ -17,14 +17,9 @@
 package android.content.pm;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.os.FileUtils;
-import android.os.Process;
 import android.os.ServiceManager;
 import android.os.UserManager;
 import android.support.test.InstrumentationRegistry;
@@ -32,7 +27,6 @@
 import android.util.Log;
 
 import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -86,12 +80,23 @@
     }
 
     @Test
+    public void testSharedInstalledPrimary() throws Exception {
+        assertEquals("1001", getContent(getKernelPackageFile("shared:android.uid.phone", "appid")));
+    }
+
+    @Test
     public void testInstalledAll() throws Exception {
         assertEquals("", getContent(getKernelPackageFile("com.android.settings",
                 "excluded_userids")));
     }
 
     @Test
+    public void testSharedInstalledAll() throws Exception {
+        assertEquals("", getContent(getKernelPackageFile("shared:android.uid.phone",
+                "excluded_userids")));
+    }
+
+    @Test
     public void testNotInstalledSecondary() throws Exception {
         mSecondaryUser = getUserManager().createUser("Secondary", 0);
         assertEquals(Integer.toString(mSecondaryUser.id),
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 44f8737..28e92db 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -146,6 +146,7 @@
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/>
+        <permission name="android.permission.GET_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
     </privapp-permissions>
 
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 0885a05..3db240b 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -83,7 +83,7 @@
 
     // ---------------------------------------------------------------------------
     // Drawing methods
-    // These are also implemented in DisplayListCanvas so that we can
+    // These are also implemented in RecordingCanvas so that we can
     // selectively apply on them
     // Everything below here is copy/pasted from Canvas.java
     // The JNI registration is handled by android_view_Canvas.cpp
@@ -549,7 +549,7 @@
                             contextStart - paraStart,
                             contextEnd - contextStart,
                             x, y, isRtl, paint.getNativeInstance(),
-                            mp.getNativeMeasuredParagraph().getNativePtr());
+                            mp.getMeasuredText().getNativePtr());
                     return;
                 }
             }
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index fb30ca2..4de7ca7 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -520,7 +520,7 @@
                             contextStart - paraStart,
                             contextEnd - contextStart,
                             x, y, isRtl, paint.getNativeInstance(),
-                            mp.getNativeMeasuredParagraph().getNativePtr());
+                            mp.getMeasuredText().getNativePtr());
                     return;
                 }
             }
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 1cd756f..632edfa 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -31,8 +31,6 @@
 import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
 import android.view.ThreadedRenderer;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -1296,8 +1294,8 @@
             final RenderNode node = RenderNode.create("BitmapTemporary", null);
             node.setLeftTopRightBottom(0, 0, width, height);
             node.setClipToBounds(false);
-            node.setAllowForceDark(false);
-            final DisplayListCanvas canvas = node.start(width, height);
+            node.setForceDarkAllowed(false);
+            final RecordingCanvas canvas = node.start(width, height);
             if (source.getWidth() != width || source.getHeight() != height) {
                 canvas.scale(width / (float) source.getWidth(),
                         height / (float) source.getHeight());
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index b65fb9c..2e1d81a 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -472,8 +472,8 @@
          *     <tr>
          *         <td>Electro-optical transfer function (EOTF)</td>
          *         <td colspan="4">\(\begin{equation}
-         *             C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.039 \\\
-         *             \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.039 \end{cases}
+         *             C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.04045 \\\
+         *             \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
          *             \end{equation}\)
          *         </td>
          *     </tr>
@@ -1484,7 +1484,7 @@
                 "Display P3",
                 new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
                 ILLUMINANT_D65,
-                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.039, 2.4),
+                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
                 Named.DISPLAY_P3.ordinal()
         );
         sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index f7acb11..f6d801b 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.UnsupportedAppUsage;
+
 import java.io.InputStream;
 import java.io.OutputStream;
 
@@ -216,7 +217,7 @@
         public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
             mPicture = pict;
-            // Disable bitmap density scaling. This matches DisplayListCanvas.
+            // Disable bitmap density scaling. This matches RecordingCanvas.
             mDensity = 0;
         }
 
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index f291e27..ea93501 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Size;
 import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
@@ -168,4 +169,14 @@
         x = in.readInt();
         y = in.readInt();
     }
+
+    /** {@hide} */
+    public static @NonNull Point convert(@NonNull Size size) {
+        return new Point(size.getWidth(), size.getHeight());
+    }
+
+    /** {@hide} */
+    public static @NonNull Size convert(@NonNull Point point) {
+        return new Size(point.x, point.y);
+    }
 }
diff --git a/core/java/android/view/DisplayListCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
similarity index 86%
rename from core/java/android/view/DisplayListCanvas.java
rename to graphics/java/android/graphics/RecordingCanvas.java
index 667fab5..7af006b 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -14,47 +14,47 @@
  * limitations under the License.
  */
 
-package android.view;
+package android.graphics;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
-import android.graphics.BaseRecordingCanvas;
-import android.graphics.Bitmap;
-import android.graphics.CanvasProperty;
-import android.graphics.Paint;
 import android.util.Pools.SynchronizedPool;
+import android.view.TextureLayer;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
 /**
  * A Canvas implementation that records view system drawing operations for deferred rendering.
- * This is intended for use with a DisplayList. This class keeps a list of all the Paint and
+ * This is intended for use with RenderNode. This class keeps a list of all the Paint and
  * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
- * the DisplayList is still holding a native reference to the memory.
+ * the RecordingCanvas is still holding a native reference to the memory.
  *
  * @hide
  */
-public final class DisplayListCanvas extends BaseRecordingCanvas {
+public final class RecordingCanvas extends BaseRecordingCanvas {
     // The recording canvas pool should be large enough to handle a deeply nested
     // view hierarchy because display lists are generated recursively.
     private static final int POOL_LIMIT = 25;
 
     public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
 
-    private static final SynchronizedPool<DisplayListCanvas> sPool =
+    private static final SynchronizedPool<RecordingCanvas> sPool =
             new SynchronizedPool<>(POOL_LIMIT);
 
-    RenderNode mNode;
+    /**
+     * TODO: Temporarily exposed for RenderNodeAnimator(Set)
+     * @hide */
+    public RenderNode mNode;
     private int mWidth;
     private int mHeight;
 
-    static DisplayListCanvas obtain(@NonNull RenderNode node, int width, int height) {
+    static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) {
         if (node == null) throw new IllegalArgumentException("node cannot be null");
-        DisplayListCanvas canvas = sPool.acquire();
+        RecordingCanvas canvas = sPool.acquire();
         if (canvas == null) {
-            canvas = new DisplayListCanvas(node, width, height);
+            canvas = new RecordingCanvas(node, width, height);
         } else {
             nResetDisplayListCanvas(canvas.mNativeCanvasWrapper, node.mNativeRenderNode,
                     width, height);
@@ -83,7 +83,7 @@
     // Constructors
     ///////////////////////////////////////////////////////////////////////////
 
-    private DisplayListCanvas(@NonNull RenderNode node, int width, int height) {
+    private RecordingCanvas(@NonNull RenderNode node, int width, int height) {
         super(nCreateDisplayListCanvas(node.mNativeRenderNode, width, height));
         mDensity = 0; // disable bitmap density scaling
     }
@@ -95,7 +95,7 @@
 
     @Override
     public void setDensity(int density) {
-        // drop silently, since DisplayListCanvas doesn't perform density scaling
+        // drop silently, since RecordingCanvas doesn't perform density scaling
     }
 
     @Override
@@ -156,6 +156,8 @@
      * functionality used by webview for calling into their renderer from our display lists.
      *
      * @param drawGLFunction A native function pointer
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public void callDrawGLFunction2(long drawGLFunction) {
@@ -166,13 +168,15 @@
      * Records the functor specified with the drawGLFunction function pointer. This is
      * functionality used by webview for calling into their renderer from our display lists.
      *
-     * @param drawGLFunction A native function pointer
+     * @param drawGLFunctor A native function pointer
      * @param releasedCallback Called when the display list is destroyed, and thus
      * the functor is no longer referenced by this canvas's display list.
      *
      * NOTE: The callback does *not* necessarily mean that there are no longer
      * any references to the functor, just that the reference from this specific
      * canvas's display list has been released.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) {
@@ -201,8 +205,9 @@
      * Draws the specified layer onto this canvas.
      *
      * @param layer The layer to composite on this canvas
+     * @hide
      */
-    void drawTextureLayer(TextureLayer layer) {
+    public void drawTextureLayer(TextureLayer layer) {
         nDrawTextureLayer(mNativeCanvasWrapper, layer.getLayerHandle());
     }
 
@@ -210,6 +215,16 @@
     // Drawing
     ///////////////////////////////////////////////////////////////////////////
 
+    /**
+     * Draws a circle
+     *
+     * @param cx
+     * @param cy
+     * @param radius
+     * @param paint
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
             CanvasProperty<Float> radius, CanvasProperty<Paint> paint) {
@@ -217,6 +232,19 @@
                 radius.getNativeContainer(), paint.getNativeContainer());
     }
 
+    /**
+     * Draws a round rect
+     *
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     * @param rx
+     * @param ry
+     * @param paint
+     *
+     * @hide
+     */
     public void drawRoundRect(CanvasProperty<Float> left, CanvasProperty<Float> top,
             CanvasProperty<Float> right, CanvasProperty<Float> bottom, CanvasProperty<Float> rx,
             CanvasProperty<Float> ry, CanvasProperty<Paint> paint) {
diff --git a/core/java/android/view/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
similarity index 92%
rename from core/java/android/view/RenderNode.java
rename to graphics/java/android/graphics/RenderNode.java
index 8ae9127..b61488c 100644
--- a/core/java/android/view/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -14,16 +14,15 @@
  * limitations under the License.
  */
 
-package android.view;
+package android.graphics;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
-import android.graphics.Matrix;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
+import android.view.NativeVectorDrawableAnimator;
+import android.view.RenderNodeAnimator;
+import android.view.View;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -36,7 +35,7 @@
 /**
  * <p>A display list records a series of graphics related operations and can replay
  * them later. Display lists are usually built by recording operations on a
- * {@link DisplayListCanvas}. Replaying the operations from a display list avoids
+ * {@link RecordingCanvas}. Replaying the operations from a display list avoids
  * executing application code on every frame, and is thus much more efficient.</p>
  *
  * <p>Display lists are used internally for all views by default, and are not
@@ -53,7 +52,7 @@
  * affected paragraph needs to be recorded again.</p>
  *
  * <h3>Hardware acceleration</h3>
- * <p>Display lists can only be replayed using a {@link DisplayListCanvas}. They are not
+ * <p>Display lists can only be replayed using a {@link RecordingCanvas}. They are not
  * supported in software. Always make sure that the {@link android.graphics.Canvas}
  * you are using to render a display list is hardware accelerated using
  * {@link android.graphics.Canvas#isHardwareAccelerated()}.</p>
@@ -63,7 +62,7 @@
  *     ThreadedRenderer renderer = myView.getThreadedRenderer();
  *     if (renderer != null) {
  *         DisplayList displayList = renderer.createDisplayList();
- *         DisplayListCanvas canvas = displayList.start(width, height);
+ *         RecordingCanvas canvas = displayList.start(width, height);
  *         try {
  *             // Draw onto the canvas
  *             // For instance: canvas.drawBitmap(...);
@@ -77,7 +76,7 @@
  * <pre class="prettyprint">
  *     protected void onDraw(Canvas canvas) {
  *         if (canvas.isHardwareAccelerated()) {
- *             DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ *             RecordingCanvas displayListCanvas = (RecordingCanvas) canvas;
  *             displayListCanvas.drawDisplayList(mDisplayList);
  *         }
  *     }
@@ -102,7 +101,7 @@
  * <pre class="prettyprint">
  *     private void createDisplayList() {
  *         mDisplayList = DisplayList.create("MyDisplayList");
- *         DisplayListCanvas canvas = mDisplayList.start(width, height);
+ *         RecordingCanvas canvas = mDisplayList.start(width, height);
  *         try {
  *             for (Bitmap b : mBitmaps) {
  *                 canvas.drawBitmap(b, 0.0f, 0.0f, null);
@@ -115,7 +114,7 @@
  *
  *     protected void onDraw(Canvas canvas) {
  *         if (canvas.isHardwareAccelerated()) {
- *             DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ *             RecordingCanvas displayListCanvas = (RecordingCanvas) canvas;
  *             displayListCanvas.drawDisplayList(mDisplayList);
  *         }
  *     }
@@ -143,10 +142,10 @@
                 RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
     }
 
-    /** Not for general use; use only if you are ThreadedRenderer or DisplayListCanvas.
+    /** Not for general use; use only if you are ThreadedRenderer or RecordingCanvas.
      * @hide
      */
-    final long mNativeRenderNode;
+    public final long mNativeRenderNode;
     private final AnimationHost mAnimationHost;
 
     private RenderNode(String name, AnimationHost animationHost) {
@@ -195,7 +194,7 @@
      *
      * @hide
      */
-    interface PositionUpdateListener {
+    public interface PositionUpdateListener {
 
         /**
          * Called by native by a Rendering Worker thread to update window position
@@ -228,7 +227,7 @@
      * stored in this display list.
      *
      * Calling this method will mark the render node invalid until
-     * {@link #end(DisplayListCanvas)} is called.
+     * {@link #end(RecordingCanvas)} is called.
      * Only valid render nodes can be replayed.
      *
      * @param width The width of the recording viewport
@@ -236,19 +235,19 @@
      *
      * @return A canvas to record drawing operations.
      *
-     * @see #end(DisplayListCanvas)
+     * @see #end(RecordingCanvas)
      * @see #isValid()
      */
     @UnsupportedAppUsage
-    public DisplayListCanvas start(int width, int height) {
-        return DisplayListCanvas.obtain(this, width, height);
+    public RecordingCanvas start(int width, int height) {
+        return RecordingCanvas.obtain(this, width, height);
     }
 
     /**
      * Same as {@link #start(int, int)} but with the RenderNode's width & height
      */
-    public DisplayListCanvas start() {
-        return DisplayListCanvas.obtain(this,
+    public RecordingCanvas start() {
+        return RecordingCanvas.obtain(this,
                 nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode));
     }
 
@@ -261,7 +260,7 @@
      * @see #isValid()
      */
     @UnsupportedAppUsage
-    public void end(DisplayListCanvas canvas) {
+    public void end(RecordingCanvas canvas) {
         long displayList = canvas.finishRecording();
         nSetDisplayList(mNativeRenderNode, displayList);
         canvas.recycle();
@@ -292,14 +291,32 @@
     // Matrix manipulation
     ///////////////////////////////////////////////////////////////////////////
 
+    /**
+     * Whether or not the RenderNode has an identity transform. This is a faster
+     * way to do the otherwise equivalent {@link #getMatrix(Matrix)} {@link Matrix#isIdentity()}
+     * as it doesn't require copying the Matrix first, thus minimizing overhead.
+     *
+     * @return true if the RenderNode has an identity transform, false otherwise
+     */
     public boolean hasIdentityMatrix() {
         return nHasIdentityMatrix(mNativeRenderNode);
     }
 
+    /**
+     * Gets the current transform matrix
+     *
+     * @param outMatrix The matrix to store the transform of the RenderNode
+     */
     public void getMatrix(@NonNull Matrix outMatrix) {
         nGetTransformMatrix(mNativeRenderNode, outMatrix.native_instance);
     }
 
+    /**
+     * Gets the current transform inverted. This is a faster way to do the otherwise
+     * equivalent {@link #getMatrix(Matrix)} followed by {@link Matrix#invert(Matrix)}
+     *
+     * @param outMatrix The matrix to store the inverse transform of the RenderNode
+     */
     public void getInverseMatrix(@NonNull Matrix outMatrix) {
         nGetInverseTransformMatrix(mNativeRenderNode, outMatrix.native_instance);
     }
@@ -308,14 +325,25 @@
     // RenderProperty Setters
     ///////////////////////////////////////////////////////////////////////////
 
+    /**
+     * TODO
+     */
     public boolean setLayerType(int layerType) {
         return nSetLayerType(mNativeRenderNode, layerType);
     }
 
+    /**
+     * TODO
+     */
     public boolean setLayerPaint(@Nullable Paint paint) {
         return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0);
     }
 
+    /**
+     * Sets the clip bounds of the RenderNode.
+     * @param rect the bounds to clip to. If null, the clip bounds are reset
+     * @return True if the clip bounds changed, false otherwise
+     */
     public boolean setClipBounds(@Nullable Rect rect) {
         if (rect == null) {
             return nSetClipBoundsEmpty(mNativeRenderNode);
@@ -371,8 +399,10 @@
             case Outline.MODE_EMPTY:
                 return nSetOutlineEmpty(mNativeRenderNode);
             case Outline.MODE_ROUND_RECT:
-                return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
-                        outline.mRect.right, outline.mRect.bottom, outline.mRadius, outline.mAlpha);
+                return nSetOutlineRoundRect(mNativeRenderNode,
+                        outline.mRect.left, outline.mRect.top,
+                        outline.mRect.right, outline.mRect.bottom,
+                        outline.mRadius, outline.mAlpha);
             case Outline.MODE_CONVEX_PATH:
                 return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath,
                         outline.mAlpha);
@@ -381,6 +411,9 @@
         throw new IllegalArgumentException("Unrecognized outline?");
     }
 
+    /**
+     * @return True if this RenderNode has a shadow, false otherwise
+     */
     public boolean hasShadow() {
         return nHasShadow(mNativeRenderNode);
     }
@@ -414,6 +447,11 @@
         return nSetClipToOutline(mNativeRenderNode, clipToOutline);
     }
 
+    /**
+     * See {@link #setClipToOutline(boolean)}
+     *
+     * @return True if this RenderNode clips to its outline, false otherwise
+     */
     public boolean getClipToOutline() {
         return nGetClipToOutline(mNativeRenderNode);
     }
@@ -518,10 +556,21 @@
         return nHasOverlappingRendering(mNativeRenderNode);
     }
 
+    /**
+     * Sets the base elevation of this RenderNode in pixels
+     *
+     * @param lift the elevation in pixels
+     * @return true if the elevation changed, false if it was the same
+     */
     public boolean setElevation(float lift) {
         return nSetElevation(mNativeRenderNode, lift);
     }
 
+    /**
+     * See {@link #setElevation(float)}
+     *
+     * @return The RenderNode's current elevation
+     */
     public float getElevation() {
         return nGetElevation(mNativeRenderNode);
     }
@@ -882,16 +931,16 @@
      * @param allow Whether or not to allow force dark.
      * @return true If the value has changed, false otherwise.
      */
-    public boolean setAllowForceDark(boolean allow) {
+    public boolean setForceDarkAllowed(boolean allow) {
         return nSetAllowForceDark(mNativeRenderNode, allow);
     }
 
     /**
-     * See {@link #setAllowForceDark(boolean)}
+     * See {@link #setForceDarkAllowed(boolean)}
      *
      * @return true if force dark is allowed (default), false if it is disabled
      */
-    public boolean getAllowForceDark() {
+    public boolean isForceDarkAllowed() {
         return nGetAllowForceDark(mNativeRenderNode);
     }
 
@@ -906,9 +955,12 @@
      * bit of a kludge.
      *
      * @hide */
-    interface AnimationHost {
+    public interface AnimationHost {
+        /** checkstyle */
         void registerAnimatingRenderNode(RenderNode animator);
+        /** checkstyle */
         void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
+        /** checkstyle */
         boolean isAttached();
     }
 
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 7ad207f..ba47300 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -27,6 +27,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
+import android.graphics.fonts.FontStyle;
 import android.graphics.fonts.FontVariationAxis;
 import android.graphics.fonts.SystemFonts;
 import android.net.Uri;
@@ -148,7 +149,7 @@
     @UnsupportedAppUsage
     private @Style int mStyle = 0;
 
-    private @IntRange(from = 0, to = android.graphics.fonts.Font.FONT_WEIGHT_MAX) int mWeight = 0;
+    private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) int mWeight = 0;
 
     // Value for weight and italic. Indicates the value is resolved by font metadata.
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 6c1372f..789e38c 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -39,7 +39,9 @@
 import android.graphics.Outline;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.os.Build;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
@@ -50,9 +52,7 @@
 import android.util.Property;
 import android.util.TimeUtils;
 import android.view.Choreographer;
-import android.view.DisplayListCanvas;
 import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNode;
 import android.view.RenderNodeAnimatorSetHelper;
 import android.view.View;
 
@@ -1542,11 +1542,11 @@
         }
 
         /**
-         * Holds a weak reference to the target that was last seen (through the DisplayListCanvas
+         * Holds a weak reference to the target that was last seen (through the RecordingCanvas
          * in the last draw call), so that when animator set needs to start, we can add the animator
          * to the last seen RenderNode target and start right away.
          */
-        protected void recordLastSeenTarget(DisplayListCanvas canvas) {
+        protected void recordLastSeenTarget(RecordingCanvas canvas) {
             final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
             mLastSeenTarget = new WeakReference<RenderNode>(node);
             // Add the animator to the list of animators on every draw
@@ -1742,7 +1742,7 @@
         @Override
         public void onDraw(Canvas canvas) {
             if (canvas.isHardwareAccelerated()) {
-                recordLastSeenTarget((DisplayListCanvas) canvas);
+                recordLastSeenTarget((RecordingCanvas) canvas);
             }
         }
 
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 626bcee..c1f8798 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -16,15 +16,8 @@
 
 package android.graphics.drawable;
 
-import android.animation.Animator;
-import android.graphics.Canvas;
-import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
-import android.view.DisplayListCanvas;
-import android.view.RenderNodeAnimator;
-
-import java.util.ArrayList;
 
 /**
  * Abstract class that handles size & positioning common to the ripple & focus states.
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index a8dc34a..cce9ba3 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -23,10 +23,10 @@
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.util.FloatProperty;
 import android.util.MathUtils;
-import android.view.DisplayListCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LinearInterpolator;
@@ -132,7 +132,7 @@
         }
     }
 
-    private void startPending(DisplayListCanvas c) {
+    private void startPending(RecordingCanvas c) {
         if (!mPendingHwAnimators.isEmpty()) {
             for (int i = 0; i < mPendingHwAnimators.size(); i++) {
                 RenderNodeAnimator animator = mPendingHwAnimators.get(i);
@@ -164,7 +164,7 @@
         }
     }
 
-    private void drawHardware(DisplayListCanvas c, Paint p) {
+    private void drawHardware(RecordingCanvas c, Paint p) {
         startPending(c);
         pruneHwFinished();
         if (mPropPaint != null) {
@@ -332,11 +332,11 @@
      * @param p the paint used to draw the ripple
      */
     public void draw(Canvas c, Paint p) {
-        final boolean hasDisplayListCanvas = !mForceSoftware && c instanceof DisplayListCanvas;
+        final boolean hasDisplayListCanvas = !mForceSoftware && c instanceof RecordingCanvas;
 
         pruneSwFinished();
         if (hasDisplayListCanvas) {
-            final DisplayListCanvas hw = (DisplayListCanvas) c;
+            final RecordingCanvas hw = (RecordingCanvas) c;
             drawHardware(hw, p);
         } else {
             drawSoftware(c, p);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index bd1ac25..f426b2d 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -51,61 +51,6 @@
     private static final int STYLE_NORMAL = 0;
 
     /**
-     * A minimum weight value for the font
-     */
-    public static final int FONT_WEIGHT_MIN = 1;
-
-    /**
-     * A font weight value for the thin weight
-     */
-    public static final int FONT_WEIGHT_THIN = 100;
-
-    /**
-     * A font weight value for the extra-light weight
-     */
-    public static final int FONT_WEIGHT_EXTRA_LIGHT = 200;
-
-    /**
-     * A font weight value for the light weight
-     */
-    public static final int FONT_WEIGHT_LIGHT = 300;
-
-    /**
-     * A font weight value for the normal weight
-     */
-    public static final int FONT_WEIGHT_NORMAL = 400;
-
-    /**
-     * A font weight value for the medium weight
-     */
-    public static final int FONT_WEIGHT_MEDIUM = 500;
-
-    /**
-     * A font weight value for the semi-bold weight
-     */
-    public static final int FONT_WEIGHT_SEMI_BOLD = 600;
-
-    /**
-     * A font weight value for the bold weight.
-     */
-    public static final int FONT_WEIGHT_BOLD = 700;
-
-    /**
-     * A font weight value for the extra-bold weight
-     */
-    public static final int FONT_WEIGHT_EXTRA_BOLD = 800;
-
-    /**
-     * A font weight value for the black weight
-     */
-    public static final int FONT_WEIGHT_BLACK = 900;
-
-    /**
-     * A maximum weight value for the font
-     */
-    public static final int FONT_WEIGHT_MAX = 1000;
-
-    /**
      * A builder class for creating new Font.
      */
     public static class Builder {
@@ -275,66 +220,68 @@
          *  <tr>
          *  <td align="center">100</td>
          *  <td align="center">Thin</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_THIN}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_THIN}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">200</td>
          *  <td align="center">Extra Light (Ultra Light)</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_EXTRA_LIGHT}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_LIGHT}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">300</td>
          *  <td align="center">Light</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_LIGHT}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_LIGHT}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">400</td>
          *  <td align="center">Normal (Regular)</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_NORMAL}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_NORMAL}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">500</td>
          *  <td align="center">Medium</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_MEDIUM}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_MEDIUM}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">600</td>
          *  <td align="center">Semi Bold (Demi Bold)</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_SEMI_BOLD}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_SEMI_BOLD}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">700</td>
          *  <td align="center">Bold</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_BOLD}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_BOLD}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">800</td>
          *  <td align="center">Extra Bold (Ultra Bold)</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_EXTRA_BOLD}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_BOLD}</td>
          *  </tr>
          *  <tr>
          *  <td align="center">900</td>
          *  <td align="center">Black (Heavy)</td>
-         *  <td align="center">{@link Font#FONT_WEIGHT_BLACK}</td>
+         *  <td align="center">{@link FontStyle#FONT_WEIGHT_BLACK}</td>
          *  </tr>
          *  </tbody>
          * </p>
          *
-         * @see Font#FONT_WEIGHT_THIN
-         * @see Font#FONT_WEIGHT_EXTRA_LIGHT
-         * @see Font#FONT_WEIGHT_LIGHT
-         * @see Font#FONT_WEIGHT_NORMAL
-         * @see Font#FONT_WEIGHT_MEDIUM
-         * @see Font#FONT_WEIGHT_SEMI_BOLD
-         * @see Font#FONT_WEIGHT_BOLD
-         * @see Font#FONT_WEIGHT_EXTRA_BOLD
-         * @see Font#FONT_WEIGHT_BLACK
+         * @see FontStyle#FONT_WEIGHT_THIN
+         * @see FontStyle#FONT_WEIGHT_EXTRA_LIGHT
+         * @see FontStyle#FONT_WEIGHT_LIGHT
+         * @see FontStyle#FONT_WEIGHT_NORMAL
+         * @see FontStyle#FONT_WEIGHT_MEDIUM
+         * @see FontStyle#FONT_WEIGHT_SEMI_BOLD
+         * @see FontStyle#FONT_WEIGHT_BOLD
+         * @see FontStyle#FONT_WEIGHT_EXTRA_BOLD
+         * @see FontStyle#FONT_WEIGHT_BLACK
          * @param weight a weight value
          * @return this builder
          */
         public @NonNull Builder setWeight(
-                @IntRange(from = FONT_WEIGHT_MIN, to = FONT_WEIGHT_MAX) int weight) {
-            Preconditions.checkArgument(FONT_WEIGHT_MIN <= weight && weight <= FONT_WEIGHT_MAX);
+                @IntRange(from = FontStyle.FONT_WEIGHT_MIN, to = FontStyle.FONT_WEIGHT_MAX)
+                int weight) {
+            Preconditions.checkArgument(
+                    FontStyle.FONT_WEIGHT_MIN <= weight && weight <= FontStyle.FONT_WEIGHT_MAX);
             mWeight = weight;
             return this;
         }
@@ -346,13 +293,12 @@
          * will resolve the style by reading font tables.
          *
          * For example, if you want to use italic font as upright font, call {@code
-         * setItalic(false)} explicitly.
+         * setSlant(false)} explicitly.
          *
-         * @param italic {@code true} if the font is italic. Otherwise {@code false}.
          * @return this builder
          */
-        public @NonNull Builder setItalic(boolean italic) {
-            mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
+        public @NonNull Builder setSlant(@FontStyle.FontSlant int slant) {
+            mItalic = slant == FontStyle.FONT_SLANT_UPRIGHT ? STYLE_NORMAL : STYLE_ITALIC;
             return this;
         }
 
@@ -414,8 +360,11 @@
                     mItalic = STYLE_NORMAL;
                 }
             }
-            mWeight = Math.max(FONT_WEIGHT_MIN, Math.min(FONT_WEIGHT_MAX, mWeight));
+            mWeight = Math.max(FontStyle.FONT_WEIGHT_MIN,
+                    Math.min(FontStyle.FONT_WEIGHT_MAX, mWeight));
             final boolean italic = (mItalic == STYLE_ITALIC);
+            final int slant = (mItalic == STYLE_ITALIC)
+                    ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT;
             final long builderPtr = nInitBuilder();
             if (mAxes != null) {
                 for (FontVariationAxis axis : mAxes) {
@@ -424,8 +373,8 @@
             }
             final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer();
             final long ptr = nBuild(builderPtr, readonlyBuffer, mWeight, italic, mTtcIndex);
-            final Font font = new Font(ptr, readonlyBuffer, mFile, mWeight, italic, mTtcIndex,
-                    mAxes, mLocaleList);
+            final Font font = new Font(ptr, readonlyBuffer, mFile,
+                    new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList);
             sFontRegistory.registerNativeAllocation(font, ptr);
             return font;
         }
@@ -454,8 +403,7 @@
     private final long mNativePtr;  // address of the shared ptr of minikin::Font
     private final @NonNull ByteBuffer mBuffer;
     private final @Nullable File mFile;
-    private final @IntRange(from = 0, to = 1000) int mWeight;
-    private final boolean mItalic;
+    private final FontStyle mFontStyle;
     private final @IntRange(from = 0) int mTtcIndex;
     private final @Nullable FontVariationAxis[] mAxes;
     private final @NonNull String mLocaleList;
@@ -464,13 +412,11 @@
      * Use Builder instead
      */
     private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
-            @IntRange(from = 0, to = 1000) int weight, boolean italic,
-            @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes,
-            @NonNull String localeList) {
+            @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex,
+            @Nullable FontVariationAxis[] axes, @NonNull String localeList) {
         mBuffer = buffer;
         mFile = file;
-        mWeight = weight;
-        mItalic = italic;
+        mFontStyle = fontStyle;
         mNativePtr = nativePtr;
         mTtcIndex = ttcIndex;
         mAxes = axes;
@@ -504,17 +450,17 @@
      * @return a weight value
      */
     public @IntRange(from = 0, to = 1000)int getWeight() {
-        return mWeight;
+        return mFontStyle.getWeight();
     }
 
     /**
-     * Returns true if this font is marked as italic, otherwise returns false.
+     * Get a slant value associated with this font.
      *
-     * @see Builder#setItalic(boolean)
-     * @return true if italic, otherwise false
+     * @see Builder#setSlant(boolean)
+     * @return a slant value
      */
-    public boolean isItalic() {
-        return mItalic;
+    public @FontStyle.FontSlant int getSlant() {
+        return mFontStyle.getSlant();
     }
 
     /**
@@ -564,21 +510,20 @@
             return false;
         }
         Font f = (Font) o;
-        return f.mWeight == mWeight && f.mItalic == mItalic && f.mTtcIndex == mTtcIndex
+        return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
                 && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mWeight, mItalic, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
+        return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
     }
 
     @Override
     public String toString() {
         return "Font {"
             + "path=" + mFile
-            + ", weight=" + mWeight
-            + ", italic=" + mItalic
+            + ", style=" + mFontStyle
             + ", ttcIndex=" + mTtcIndex
             + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
             + ", localeList=" + mLocaleList
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 3bcdc31..52a37da 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -124,7 +124,7 @@
         }
 
         private static int makeStyleIdentifier(@NonNull Font font) {
-            return font.getWeight() | (font.isItalic() ? (1 << 16) : 0);
+            return font.getWeight() | (font.getSlant()  << 16);
         }
 
         private static native long nInitBuilder();
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
new file mode 100644
index 0000000..82fc7ac
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A font style object.
+ *
+ * This class represents a single font style which is a pair of weight value and slant value.
+ * Here are common font styles examples:
+ * <p>
+ * <pre>
+ * <code>
+ * final FontStyle NORMAL = new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT);
+ * final FontStyle BOLD = new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT);
+ * final FontStyle ITALIC = new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC);
+ * final FontStyle BOLD_ITALIC = new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_ITALIC);
+ * </code>
+ * </pre>
+ * </p>
+ *
+ */
+public final class FontStyle {
+    private static final String TAG = "FontStyle";
+
+    /**
+     * A minimum weight value for the font
+     */
+    public static final int FONT_WEIGHT_MIN = 1;
+
+    /**
+     * A font weight value for the thin weight
+     */
+    public static final int FONT_WEIGHT_THIN = 100;
+
+    /**
+     * A font weight value for the extra-light weight
+     */
+    public static final int FONT_WEIGHT_EXTRA_LIGHT = 200;
+
+    /**
+     * A font weight value for the light weight
+     */
+    public static final int FONT_WEIGHT_LIGHT = 300;
+
+    /**
+     * A font weight value for the normal weight
+     */
+    public static final int FONT_WEIGHT_NORMAL = 400;
+
+    /**
+     * A font weight value for the medium weight
+     */
+    public static final int FONT_WEIGHT_MEDIUM = 500;
+
+    /**
+     * A font weight value for the semi-bold weight
+     */
+    public static final int FONT_WEIGHT_SEMI_BOLD = 600;
+
+    /**
+     * A font weight value for the bold weight.
+     */
+    public static final int FONT_WEIGHT_BOLD = 700;
+
+    /**
+     * A font weight value for the extra-bold weight
+     */
+    public static final int FONT_WEIGHT_EXTRA_BOLD = 800;
+
+    /**
+     * A font weight value for the black weight
+     */
+    public static final int FONT_WEIGHT_BLACK = 900;
+
+    /**
+     * A maximum weight value for the font
+     */
+    public static final int FONT_WEIGHT_MAX = 1000;
+
+    /**
+     * A font slant value for upright
+     */
+    public static final int FONT_SLANT_UPRIGHT = 0;
+
+    /**
+     * A font slant value for italic
+     */
+    public static final int FONT_SLANT_ITALIC = 1;
+
+    // TODO: Support FONT_SLANT_OBLIQUE
+
+    /** @hide */
+    @IntDef(prefix = { "FONT_SLANT_" }, value = {
+            FONT_SLANT_UPRIGHT,
+            FONT_SLANT_ITALIC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FontSlant {}
+
+    private final @IntRange(from = 0, to = 1000) int mWeight;
+    private final @FontSlant int mSlant;
+    // TODO: Support width
+
+    public FontStyle() {
+        mWeight = FONT_WEIGHT_NORMAL;
+        mSlant = FONT_SLANT_UPRIGHT;
+    }
+
+    /**
+     * Create FontStyle with specific weight and italic
+     *
+     * <p>
+     *  <table>
+     *  <thead>
+     *  <tr>
+     *  <th align="center">Value</th>
+     *  <th align="center">Name</th>
+     *  <th align="center">Android Definition</th>
+     *  </tr>
+     *  </thead>
+     *  <tbody>
+     *  <tr>
+     *  <td align="center">100</td>
+     *  <td align="center">Thin</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_THIN}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">200</td>
+     *  <td align="center">Extra Light (Ultra Light)</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_LIGHT}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">300</td>
+     *  <td align="center">Light</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_LIGHT}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">400</td>
+     *  <td align="center">Normal (Regular)</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_NORMAL}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">500</td>
+     *  <td align="center">Medium</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_MEDIUM}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">600</td>
+     *  <td align="center">Semi Bold (Demi Bold)</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_SEMI_BOLD}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">700</td>
+     *  <td align="center">Bold</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_BOLD}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">800</td>
+     *  <td align="center">Extra Bold (Ultra Bold)</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_EXTRA_BOLD}</td>
+     *  </tr>
+     *  <tr>
+     *  <td align="center">900</td>
+     *  <td align="center">Black (Heavy)</td>
+     *  <td align="center">{@link FontStyle#FONT_WEIGHT_BLACK}</td>
+     *  </tr>
+     *  </tbody>
+     * </p>
+     *
+     * @see FontStyle#FONT_WEIGHT_THIN
+     * @see FontStyle#FONT_WEIGHT_EXTRA_LIGHT
+     * @see FontStyle#FONT_WEIGHT_LIGHT
+     * @see FontStyle#FONT_WEIGHT_NORMAL
+     * @see FontStyle#FONT_WEIGHT_MEDIUM
+     * @see FontStyle#FONT_WEIGHT_SEMI_BOLD
+     * @see FontStyle#FONT_WEIGHT_BOLD
+     * @see FontStyle#FONT_WEIGHT_EXTRA_BOLD
+     * @see FontStyle#FONT_WEIGHT_BLACK
+     * @param weight a weight value
+     * @param slant a slant value
+     */
+    public FontStyle(int weight, @FontSlant int slant) {
+        Preconditions.checkArgument(FONT_WEIGHT_MIN <= weight && weight <= FONT_WEIGHT_MAX,
+                "weight value must be [" + FONT_WEIGHT_MIN + ", " + FONT_WEIGHT_MAX + "]");
+        Preconditions.checkArgument(slant == FONT_SLANT_UPRIGHT || slant == FONT_SLANT_ITALIC,
+                "slant value must be FONT_SLANT_UPRIGHT or FONT_SLANT_UPRIGHT");
+        mWeight = weight;
+        mSlant = slant;
+    }
+
+
+     /**
+      * Gets the weight value
+      *
+      * @see FontStyle#setWeight(int)
+      * @return a weight value
+      */
+    public @IntRange(from = 0, to = 1000) int getWeight() {
+        return mWeight;
+    }
+
+    /**
+     * Gets the slant value
+     *
+     * @return a slant value
+     */
+    public @FontSlant int getSlant() {
+        return mSlant;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (o == null || !(o instanceof FontStyle)) {
+            return false;
+        }
+        FontStyle fontStyle = (FontStyle) o;
+        return fontStyle.mWeight == mWeight && fontStyle.mSlant == mSlant;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mWeight, mSlant);
+    }
+
+    @Override
+    public String toString() {
+        return "FontStyle { weight=" + mWeight + ", slant=" + mSlant + "}";
+    }
+}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 2d21bbb..750adb2 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -192,7 +192,8 @@
             try {
                 font = new Font.Builder(buffer, new File(fullPath), languageTags)
                         .setWeight(fontConfig.getWeight())
-                        .setItalic(fontConfig.isItalic())
+                        .setSlant(fontConfig.isItalic() ? FontStyle.FONT_SLANT_ITALIC
+                                : FontStyle.FONT_SLANT_UPRIGHT)
                         .setTtcIndex(fontConfig.getTtcIndex())
                         .setFontVariationSettings(fontConfig.getAxes())
                         .build();
diff --git a/core/java/android/text/NativeLineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
similarity index 67%
rename from core/java/android/text/NativeLineBreaker.java
rename to graphics/java/android/graphics/text/LineBreaker.java
index 94e10e8..1647909 100644
--- a/core/java/android/text/NativeLineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.text;
+package android.graphics.text;
 
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
@@ -32,11 +32,60 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * A native implementation of the line breaker.
- * TODO: Consider to make this class public.
- * @hide
+ * Provides automatic line breaking for a <em>single</em> paragraph.
+ *
+ * <p>
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
+ * String text = "Hello, Android.";
+ *
+ * // Prepare the measured text
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ *     .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
+ *     .appednStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
+ *     .build();
+ *
+ * LineBreaker lb = new LineBreaker.Builder()
+ *     // Use simple line breaker
+ *     .setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE)
+ *     // Do not add hyphenation.
+ *     .setHyphenationFrequency(LineBreaker.HYPHENATION_FREQUENCY_NONE)
+ *     // Build the LineBreaker
+ *     .build();
+ *
+ * ParagraphConstraints c = new ParagraphConstraints();
+ * c.setWidth(240);  // Set the line wieth as 1024px
+ *
+ * // Do the line breaking
+ * Result r = lb.computeLineBreaks(mt, c, 0);
+ *
+ * // Compute the total height of the text.
+ * float totalHeight = 0;
+ * for (int i = 0; i < r.getLineCount(); ++i) {  // iterate over the lines
+ *    totalHeight += r.getLineDescent(i) - r.getLineAscent(i);
+ * }
+ *
+ * // Draw text to the canvas
+ * Bitmap bmp = new Bitmap.createBitmap(240, totalHeight, Bitmap.Config.ARGB_8888);
+ * Canvas c = new Canvas(bmp);
+ * float yOffset = 0f;
+ * int prevOffset = 0;
+ * for (int i = 0; i < r.getLineCount(); ++i) {  // iterate over the lines
+ *     int nextOffset = r.getLineBreakOffset(i);
+ *     c.drawText(text, prevOffset, nextOffset, 0f, yOffset, paint);
+ *
+ *     prevOffset = nextOffset;
+ *     yOffset += r.getLineDescent(i) - r.getLineAscent(i);
+ * }
+ * </code>
+ * </pre>
+ * </p>
  */
-public class NativeLineBreaker {
+public class LineBreaker {
+    /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
             BREAK_STRATEGY_SIMPLE,
             BREAK_STRATEGY_HIGH_QUALITY,
@@ -46,25 +95,33 @@
     public @interface BreakStrategy {}
 
     /**
-     * Value for break strategy indicating simple line breaking. Automatic hyphens are not added
-     * (though soft hyphens are respected), and modifying text generally doesn't affect the layout
-     * before it (which yields a more consistent user experience when editing), but layout may not
-     * be the highest quality.
+     * Value for break strategy indicating simple line breaking.
+     *
+     * The line breaker puts words to the line as much as possible and breaks line if no more words
+     * can fit into the same line. Automatic hyphens are only added when a line has a single word
+     * and that word is longer than line width. This is the fastest break strategy and ideal for
+     * editor.
      */
     public static final int BREAK_STRATEGY_SIMPLE = 0;
 
     /**
-     * Value for break strategy indicating high quality line breaking, including automatic
-     * hyphenation and doing whole-paragraph optimization of line breaks.
+     * Value for break strategy indicating high quality line breaking.
+     *
+     * With this option line breaker does whole-paragraph optimization for more readable text, and
+     * also applies automatic hyphenation when required.
      */
     public static final int BREAK_STRATEGY_HIGH_QUALITY = 1;
 
     /**
-     * Value for break strategy indicating balanced line breaking. The breaks are chosen to
-     * make all lines as close to the same length as possible, including automatic hyphenation.
+     * Value for break strategy indicating balanced line breaking.
+     *
+     * The line breaker does whole-paragraph optimization for making all lines similar length, and
+     * also applies automatic hyphenation when required. This break strategy is good for small
+     * screen devices such as watch screens.
      */
     public static final int BREAK_STRATEGY_BALANCED = 2;
 
+    /** @hide */
     @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = {
             HYPHENATION_FREQUENCY_NORMAL,
             HYPHENATION_FREQUENCY_FULL,
@@ -74,28 +131,32 @@
     public @interface HyphenationFrequency {}
 
     /**
-     * Value for hyphenation frequency indicating no automatic hyphenation. Useful
-     * for backward compatibility, and for cases where the automatic hyphenation algorithm results
-     * in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the
-     * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used
-     * as suggestions for potential line breaks.
+     * Value for hyphenation frequency indicating no automatic hyphenation.
+     *
+     * Using this option disables auto hyphenation which results in better text layout performance.
+     * A word may be broken without hyphens when a line has a single word and that word is longer
+     * than line width. Soft hyphens are ignored and will not be used as suggestions for potential
+     * line breaks.
      */
     public static final int HYPHENATION_FREQUENCY_NONE = 0;
 
     /**
-     * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which
-     * is a conservative default. Useful for informal cases, such as short sentences or chat
+     * Value for hyphenation frequency indicating a light amount of automatic hyphenation.
+     *
+     * This hyphenation frequency is useful for informal cases, such as short sentences or chat
      * messages.
      */
     public static final int HYPHENATION_FREQUENCY_NORMAL = 1;
 
     /**
-     * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical
-     * in typography. Useful for running text and where it's important to put the maximum amount of
-     * text in a screen with limited space.
+     * Value for hyphenation frequency indicating the full amount of automatic hyphenation.
+     *
+     * This hyphenation frequency is useful for running text and where it's important to put the
+     * maximum amount of text in a screen with limited space.
      */
     public static final int HYPHENATION_FREQUENCY_FULL = 2;
 
+    /** @hide */
     @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = {
             JUSTIFICATION_MODE_NONE,
             JUSTIFICATION_MODE_INTER_WORD
@@ -114,7 +175,7 @@
     public static final int JUSTIFICATION_MODE_INTER_WORD = 1;
 
     /**
-     * A builder class of NativeLineBreaker.
+     * Helper class for creating a {@link LineBreaker}.
      */
     public static class Builder {
         private @BreakStrategy int mBreakStrategy = BREAK_STRATEGY_SIMPLE;
@@ -123,12 +184,10 @@
         private @Nullable int[] mIndents = null;
 
         /**
-         * Construct a builder class.
-         */
-        public Builder() {}
-
-        /**
          * Set break strategy.
+         *
+         * You can change the line breaking behavior by setting break strategy. The default value is
+         * {@link #BREAK_STRATEGY_SIMPLE}.
          */
         public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
             mBreakStrategy = breakStrategy;
@@ -137,6 +196,9 @@
 
         /**
          * Set hyphenation frequency.
+         *
+         * You can change the amount of automatic hyphenation used. The default value is
+         * {@link #HYPHENATION_FREQUENCY_NONE}.
          */
         public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
             mHyphenationFrequency = hyphenationFrequency;
@@ -145,6 +207,10 @@
 
         /**
          * Set whether the text is justified.
+         *
+         * By setting {@link #JUSTIFICATION_MODE_INTER_WORD}, the line breaker will change the
+         * internal parameters for justification.
+         * The default value is {@link #JUSTIFICATION_MODE_NONE}
          */
         public Builder setJustified(@JustificationMode int justified) {
             mJustified = justified;
@@ -152,9 +218,11 @@
         }
 
         /**
-         * Set indents for entire text.
+         * Set indents.
          *
-         * Sets the total (left + right) indents in pixel per lines.
+         * The supplied array provides the total amount of indentation per line, in pixel. This
+         * amount is the sum of both left and right indentations. For lines past the last element in
+         * the array, the indentation amount of the last element is used.
          */
         public Builder setIndents(@Nullable int[] indents) {
             mIndents = indents;
@@ -162,11 +230,12 @@
         }
 
         /**
-         * Returns the NativeLineBreaker with given parameters.
+         * Build a new LineBreaker with given parameters.
+         *
+         * You can reuse the Builder instance even after calling this method.
          */
-        NativeLineBreaker build() {
-            return new NativeLineBreaker(mBreakStrategy, mHyphenationFrequency, mJustified,
-                    mIndents);
+        public LineBreaker build() {
+            return new LineBreaker(mBreakStrategy, mHyphenationFrequency, mJustified, mIndents);
         }
     }
 
@@ -184,8 +253,10 @@
 
         /**
          * Set width for this paragraph.
+         *
+         * @see #getWidth()
          */
-        public void setWidth(@FloatRange(from = 0.0f) float width) {
+        public void setWidth(@Px @FloatRange(from = 0.0f) float width) {
             mWidth = width;
         }
 
@@ -194,9 +265,11 @@
          *
          * @param firstWidth the line width of the starting of the paragraph
          * @param firstWidthLineCount the number of lines that applies the firstWidth
+         * @see #getFirstWidth()
+         * @see #getFirstWidthLineCount()
          */
-        public void setIndent(@FloatRange(from = 0.0f) float firstWidth,
-                @IntRange(from = 0) int firstWidthLineCount) {
+        public void setIndent(@Px @FloatRange(from = 0.0f) float firstWidth,
+                @Px @IntRange(from = 0) int firstWidthLineCount) {
             mFirstWidth = firstWidth;
             mFirstWidthLineCount = firstWidthLineCount;
         }
@@ -206,16 +279,21 @@
          *
          * @param tabStops the array of pixels of tap stopping position
          * @param defaultTabStop pixels of the default tab stopping position
+         * @see #getTabStops()
+         * @see #getDefaultTabStop()
          */
-        public void setTabStops(@Nullable int[] tabStops, @IntRange(from = 0) int defaultTabStop) {
+        public void setTabStops(@Nullable int[] tabStops,
+                @Px @IntRange(from = 0) int defaultTabStop) {
             mVariableTabStops = tabStops;
             mDefaultTabStop = defaultTabStop;
         }
 
         /**
          * Return the width for this paragraph in pixels.
+         *
+         * @see #setWidth(float)
          */
-        public @FloatRange(from = 0.0f) float getWidth() {
+        public @Px @FloatRange(from = 0.0f) float getWidth() {
             return mWidth;
         }
 
@@ -224,7 +302,7 @@
          *
          * @see #setIndent(float, int)
          */
-        public @FloatRange(from = 0.0f) float getFirstWidth() {
+        public @Px @FloatRange(from = 0.0f) float getFirstWidth() {
             return mFirstWidth;
         }
 
@@ -233,7 +311,7 @@
          *
          * @see #setIndent(float, int)
          */
-        public @IntRange(from = 0) int getFirstWidthLineCount() {
+        public @Px @IntRange(from = 0) int getFirstWidthLineCount() {
             return mFirstWidthLineCount;
         }
 
@@ -251,13 +329,14 @@
          *
          * @see #setTabStop(int[], int)
          */
-        public @IntRange(from = 0) int getDefaultTabStop() {
+        public @Px @IntRange(from = 0) int getDefaultTabStop() {
             return mDefaultTabStop;
         }
     }
 
     /**
-     * A result object of a line breaking
+     * Holds the result of the {@link LineBreaker#computeLineBreaks line breaking algorithm}.
+     * @see LineBreaker#computeLineBreaks
      */
     public static class Result {
         // Following two contstant must be synced with minikin's line breaker.
@@ -274,7 +353,7 @@
         }
 
         /**
-         * Returns a number of line count.
+         * Returns the number of lines in the paragraph.
          *
          * @return number of lines
          */
@@ -283,7 +362,7 @@
         }
 
         /**
-         * Returns a break offset of the line.
+         * Returns character offset of the break for a given line.
          *
          * @param lineIndex an index of the line.
          * @return the break offset.
@@ -293,17 +372,17 @@
         }
 
         /**
-         * Returns a width of the line in pixels.
+         * Returns width of a given line in pixels.
          *
          * @param lineIndex an index of the line.
-         * @return a width of the line in pixexls
+         * @return width of the line in pixels
          */
         public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) {
             return nGetLineWidth(mPtr, lineIndex);
         }
 
         /**
-         * Returns an entier font ascent of the line in pixels.
+         * Returns font ascent of the line in pixels.
          *
          * @param lineIndex an index of the line.
          * @return an entier font ascent of the line in pixels.
@@ -313,7 +392,7 @@
         }
 
         /**
-         * Returns an entier font descent of the line in pixels.
+         * Returns font descent of the line in pixels.
          *
          * @param lineIndex an index of the line.
          * @return an entier font descent of the line in pixels.
@@ -337,6 +416,7 @@
          *
          * @param lineIndex an index of the line.
          * @return a packed hyphen edit for the line.
+         *
          * @see android.text.Hyphenator#unpackStartHyphenEdit(int)
          * @see android.text.Hyphenator#unpackEndHyphenEdit(int)
          * @see android.text.Hyphenator#packHyphenEdit(int,int)
@@ -347,14 +427,14 @@
     }
 
     private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
-            NativeLineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
+            LineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
 
     private final long mNativePtr;
 
     /**
      * Use Builder instead.
      */
-    private NativeLineBreaker(@BreakStrategy int breakStrategy,
+    private LineBreaker(@BreakStrategy int breakStrategy,
             @HyphenationFrequency int hyphenationFrequency, @JustificationMode int justify,
             @Nullable int[] indents) {
         mNativePtr = nInit(breakStrategy, hyphenationFrequency,
@@ -372,7 +452,7 @@
      * @param lineNumber a line number of this paragraph
      */
     public Result computeLineBreaks(
-            @NonNull NativeMeasuredParagraph measuredPara,
+            @NonNull MeasuredText measuredPara,
             @NonNull ParagraphConstraints constraints,
             @IntRange(from = 0) int lineNumber) {
         return new Result(nComputeLineBreaks(
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
new file mode 100644
index 0000000..3efe655
--- /dev/null
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2010 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.graphics.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Result of text shaping of the single paragraph string.
+ *
+ * <p>
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * Paint bigPaint = new Paint();
+ * bigPaint.setTextSize(paint.getTextSize() * 2.0);
+ * String text = "Hello, Android.";
+ * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+ *      .appendStyleRun(paint, 7, false)  // Use paint for "Hello, "
+ *      .appendStyleRun(bigPaint, 8, false)  // Use bigPaint for "Hello, "
+ *      .build();
+ * </code>
+ * </pre>
+ * </p>
+ */
+public class MeasuredText {
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+    private long mNativePtr;
+    private @NonNull char[] mChars;
+
+    // Use builder instead.
+    private MeasuredText(long ptr, @NonNull char[] chars) {
+        mNativePtr = ptr;
+        mChars = chars;
+    }
+
+    /**
+     * Returns the characters in the paragraph used to compute this MeasuredText instance.
+     */
+    public @NonNull char[] getChars() {
+        return mChars;
+    }
+
+    /**
+     * Returns the width of a given range.
+     *
+     * @param start an inclusive start index of the range
+     * @param end an exclusive end index of the range
+     */
+    public @FloatRange(from = 0.0) @Px float getWidth(
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+        Preconditions.checkArgument(0 <= start && start <= mChars.length,
+                "start(" + start + ") must be 0 <= start <= " + mChars.length);
+        Preconditions.checkArgument(0 <= end && end <= mChars.length,
+                "end(" + end + ") must be 0 <= end <= " + mChars.length);
+        Preconditions.checkArgument(start <= end,
+                "start(" + start + ") is larger than end(" + end + ")");
+        return nGetWidth(mNativePtr, start, end);
+    }
+
+    /**
+     * Returns a memory usage of the native object.
+     *
+     * @hide
+     */
+    public int getMemoryUsage() {
+        return nGetMemoryUsage(mNativePtr);
+    }
+
+    /**
+     * Retrieves the boundary box of the given range
+     *
+     * @param start an inclusive start index of the range
+     * @param end an exclusive end index of the range
+     * @param rect an output parameter
+     */
+    public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Rect rect) {
+        Preconditions.checkArgument(0 <= start && start <= mChars.length,
+                "start(" + start + ") must be 0 <= start <= " + mChars.length);
+        Preconditions.checkArgument(0 <= end && end <= mChars.length,
+                "end(" + end + ") must be 0 <= end <= " + mChars.length);
+        Preconditions.checkArgument(start <= end,
+                "start(" + start + ") is larger than end(" + end + ")");
+        Preconditions.checkNotNull(rect);
+        nGetBounds(mNativePtr, mChars, start, end, rect);
+    }
+
+    /**
+     * Returns the width of the character at the given offset.
+     *
+     * @param offset an offset of the character.
+     */
+    public @FloatRange(from = 0.0f) @Px float getCharWidthAt(@IntRange(from = 0) int offset) {
+        Preconditions.checkArgument(0 <= offset && offset < mChars.length,
+                "offset(" + offset + ") is larger than text length: " + mChars.length);
+        return nGetCharWidthAt(mNativePtr, offset);
+    }
+
+    /**
+     * Returns a native pointer of the underlying native object.
+     *
+     * @hide
+     */
+    public long getNativePtr() {
+        return mNativePtr;
+    }
+
+    @CriticalNative
+    private static native float nGetWidth(/* Non Zero */ long nativePtr,
+                                         @IntRange(from = 0) int start,
+                                         @IntRange(from = 0) int end);
+
+    @CriticalNative
+    private static native /* Non Zero */ long nGetReleaseFunc();
+
+    @CriticalNative
+    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+            Rect rect);
+
+    @CriticalNative
+    private static native float nGetCharWidthAt(long nativePtr, int offset);
+
+    /**
+     * Helper class for creating a {@link MeasuredText}.
+     * <p>
+     * <pre>
+     * <code>
+     * Paint paint = new Paint();
+     * String text = "Hello, Android.";
+     * MeasuredText mt = new MeasuredText.Builder(text.toCharArray())
+     *      .appendStyleRun(paint, text.length, false)
+     *      .build();
+     * </code>
+     * </pre>
+     * </p>
+     *
+     * Note: The appendStyle and appendReplacementRun should be called to cover the text length.
+     */
+    public static class Builder {
+        private long mNativePtr;
+
+        private final @NonNull char[] mText;
+        private boolean mComputeHyphenation = false;
+        private boolean mComputeLayout = true;
+        private int mCurrentOffset = 0;
+
+        /**
+         * Construct a builder.
+         *
+         * The MeasuredText returned by build method will hold a reference of the text. Developer is
+         * not supposed to modify the text.
+         *
+         * @param text a text
+         */
+        public Builder(@NonNull char[] text) {
+            Preconditions.checkNotNull(text);
+            mText = text;
+            mNativePtr = nInitBuilder();
+        }
+
+        /**
+         * Apply styles to the given length.
+         *
+         * Keeps an internal offset which increases at every append. The initial value for this
+         * offset is zero. After the style is applied the internal offset is moved to {@code offset
+         * + length}, and next call will start from this new position.
+         *
+         * @param paint a paint
+         * @param length a length to be applied with a given paint, can not exceed the length of the
+         *               text
+         * @param isRtl true if the text is in RTL context, otherwise false.
+         */
+        public Builder appendStyleRun(@NonNull Paint paint, @IntRange(from = 0) int length,
+                boolean isRtl) {
+            Preconditions.checkNotNull(paint);
+            Preconditions.checkArgument(length > 0, "length can not be negative");
+            final int end = mCurrentOffset + length;
+            Preconditions.checkArgument(end <= mText.length, "Style exceeds the text length");
+            nAddStyleRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, isRtl);
+            mCurrentOffset = end;
+            return this;
+        }
+
+        /**
+         * Used to inform the text layout that the given length is replaced with the object of given
+         * width.
+         *
+         * Keeps an internal offset which increases at every append. The initial value for this
+         * offset is zero. After the style is applied the internal offset is moved to {@code offset
+         * + length}, and next call will start from this new position.
+         *
+         * Informs the layout engine that the given length should not be processed, instead the
+         * provided width should be used for calculating the width of that range.
+         *
+         * @param length a length to be replaced with the object, can not exceed the length of the
+         *               text
+         * @param width a replacement width of the range
+         */
+        public Builder appendReplacementRun(@NonNull Paint paint,
+                @IntRange(from = 0) int length, @FloatRange(from = 0) float width) {
+            Preconditions.checkArgument(length > 0, "length can not be negative");
+            final int end = mCurrentOffset + length;
+            Preconditions.checkArgument(end <= mText.length, "Replacement exceeds the text length");
+            nAddReplacementRun(mNativePtr, paint.getNativeInstance(), mCurrentOffset, end, width);
+            mCurrentOffset = end;
+            return this;
+        }
+
+        /**
+         * By passing true to this method, the build method will compute all possible hyphenation
+         * pieces as well.
+         *
+         * If you don't want to use automatic hyphenation, you can pass false to this method and
+         * save the computation time of hyphenation. The default value is false.
+         *
+         * Even if you pass false to this method, you can still enable automatic hyphenation of
+         * LineBreaker but line break computation becomes slower.
+         *
+         * @param computeHyphenation true if you want to use automatic hyphenations.
+         */
+        public Builder setComputeHyphenation(boolean computeHyphenation) {
+            mComputeHyphenation = computeHyphenation;
+            return this;
+        }
+
+        /**
+         * By passing true to this method, the build method will compute all full layout
+         * information.
+         *
+         * If you don't use {@link MeasuredText#getBounds(int,int,android.graphics.Rect)}, you can
+         * pass false to this method and save the memory spaces. The default value is true.
+         *
+         * Even if you pass false to this method, you can still call getBounds but it becomes
+         * slower.
+         *
+         * @param computeLayout true if you want to retrieve full layout info, e.g. bbox.
+         */
+        public Builder setComputeLayout(boolean computeLayout) {
+            mComputeLayout = computeLayout;
+            return this;
+        }
+
+        /**
+         * Creates a MeasuredText.
+         *
+         * Once you called build() method, you can't reuse the Builder class again.
+         * @throws IllegalStateException if this Builder is reused.
+         * @throws IllegalStateException if the whole text is not covered by one or more runs (style
+         *                               or replacement)
+         */
+        public MeasuredText build() {
+            ensureNativePtrNoReuse();
+            if (mCurrentOffset != mText.length) {
+                throw new IllegalStateException("Style info has not been provided for all text.");
+            }
+            try {
+                long ptr = nBuildMeasuredText(mNativePtr, mText, mComputeHyphenation,
+                        mComputeLayout);
+                MeasuredText res = new MeasuredText(ptr, mText);
+                sRegistry.registerNativeAllocation(res, ptr);
+                return res;
+            } finally {
+                nFreeBuilder(mNativePtr);
+                mNativePtr = 0;
+            }
+        }
+
+        /**
+         * Ensures {@link #mNativePtr} is not reused.
+         *
+         * <p/> This is a method by itself to help increase testability - eg. Robolectric might want
+         * to override the validation behavior in test environment.
+         */
+        private void ensureNativePtrNoReuse() {
+            if (mNativePtr == 0) {
+                throw new IllegalStateException("Builder can not be reused.");
+            }
+        }
+
+        private static native /* Non Zero */ long nInitBuilder();
+
+        /**
+         * Apply style to make native measured text.
+         *
+         * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+         * @param paintPtr The native paint pointer to be applied.
+         * @param start The start offset in the copied buffer.
+         * @param end The end offset in the copied buffer.
+         * @param isRtl True if the text is RTL.
+         */
+        private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+                                                /* Non Zero */ long paintPtr,
+                                                @IntRange(from = 0) int start,
+                                                @IntRange(from = 0) int end,
+                                                boolean isRtl);
+        /**
+         * Apply ReplacementRun to make native measured text.
+         *
+         * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+         * @param paintPtr The native paint pointer to be applied.
+         * @param start The start offset in the copied buffer.
+         * @param end The end offset in the copied buffer.
+         * @param width The width of the replacement.
+         */
+        private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+                                                      /* Non Zero */ long paintPtr,
+                                                      @IntRange(from = 0) int start,
+                                                      @IntRange(from = 0) int end,
+                                                      @FloatRange(from = 0) float width);
+
+        private static native long nBuildMeasuredText(
+                /* Non Zero */ long nativeBuilderPtr,
+                @NonNull char[] text,
+                boolean computeHyphenation,
+                boolean computeLayout);
+
+        private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+    }
+}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 8f58f74..66a5477 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -39,7 +39,7 @@
 
 static const std::string kResourcesArsc("resources.arsc");
 
-ApkAssets::ApkAssets(void* unmanaged_handle, const std::string& path)
+ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path)
     : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) {
 }
 
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 69702e3..db2d038 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -27,6 +27,9 @@
 #include "androidfw/LoadedArsc.h"
 #include "androidfw/misc.h"
 
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
+
 namespace android {
 
 class LoadedIdmap;
@@ -88,9 +91,9 @@
   // Creates an Asset from any file on the file system.
   static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
 
-  ApkAssets(void* unmanaged_handle, const std::string& path);
+  ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path);
 
-  using ZipArchivePtr = std::unique_ptr<void, void(*)(void*)>;
+  using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
 
   ZipArchivePtr zip_handle_;
   const std::string path_;
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index 03154d0..c221e3b 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -41,7 +41,8 @@
 #include <unistd.h>
 #include <time.h>
 
-typedef void* ZipArchiveHandle;
+struct ZipArchive;
+typedef ZipArchive* ZipArchiveHandle;
 
 namespace android {
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e3ec45b..f0053a4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -202,9 +202,7 @@
         "AnimationContext.cpp",
         "Animator.cpp",
         "AnimatorManager.cpp",
-        "CanvasState.cpp",
         "CanvasTransform.cpp",
-        "ClipArea.cpp",
         "DamageAccumulator.cpp",
         "DeferredLayerUpdater.cpp",
         "DeviceInfo.cpp",
@@ -227,14 +225,15 @@
         "RecordingCanvas.cpp",
         "RenderNode.cpp",
         "RenderProperties.cpp",
-        "ResourceCache.cpp",
         "SkiaCanvas.cpp",
-        "Snapshot.cpp",
         "TreeInfo.cpp",
         "VectorDrawable.cpp",
         "protos/graphicsstats.proto",
     ],
 
+    // Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
+    cflags: ["-Wno-implicit-fallthrough"],
+
     proto: {
         export_proto_headers: true,
     },
@@ -308,8 +307,6 @@
         "tests/unit/main.cpp",
         "tests/unit/CacheManagerTests.cpp",
         "tests/unit/CanvasContextTests.cpp",
-        "tests/unit/CanvasStateTests.cpp",
-        "tests/unit/ClipAreaTests.cpp",
         "tests/unit/DamageAccumulatorTests.cpp",
         "tests/unit/DeferredLayerUpdaterTests.cpp",
         "tests/unit/FatVectorTests.cpp",
@@ -328,7 +325,6 @@
         "tests/unit/SkiaPipelineTests.cpp",
         "tests/unit/SkiaRenderPropertiesTests.cpp",
         "tests/unit/SkiaCanvasTests.cpp",
-        "tests/unit/SnapshotTests.cpp",
         "tests/unit/StringUtilsTests.cpp",
         "tests/unit/TestUtilsTests.cpp",
         "tests/unit/ThreadBaseTests.cpp",
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
deleted file mode 100644
index d18c4ab..0000000
--- a/libs/hwui/CanvasState.cpp
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * 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.
- */
-
-#include "CanvasState.h"
-#include "hwui/Canvas.h"
-#include "utils/MathUtils.h"
-
-namespace android {
-namespace uirenderer {
-
-CanvasState::CanvasState(CanvasStateClient& renderer)
-        : mWidth(-1), mHeight(-1), mSaveCount(1), mCanvas(renderer), mSnapshot(&mFirstSnapshot) {}
-
-CanvasState::~CanvasState() {
-    // First call freeSnapshot on all but mFirstSnapshot
-    // to invoke all the dtors
-    freeAllSnapshots();
-
-    // Now actually release the memory
-    while (mSnapshotPool) {
-        void* temp = mSnapshotPool;
-        mSnapshotPool = mSnapshotPool->previous;
-        free(temp);
-    }
-}
-
-void CanvasState::initializeRecordingSaveStack(int viewportWidth, int viewportHeight) {
-    if (mWidth != viewportWidth || mHeight != viewportHeight) {
-        mWidth = viewportWidth;
-        mHeight = viewportHeight;
-        mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
-        mCanvas.onViewportInitialized();
-    }
-
-    freeAllSnapshots();
-    mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
-    mSnapshot->setRelativeLightCenter(Vector3());
-    mSaveCount = 1;
-}
-
-void CanvasState::initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft,
-                                      float clipTop, float clipRight, float clipBottom,
-                                      const Vector3& lightCenter) {
-    if (mWidth != viewportWidth || mHeight != viewportHeight) {
-        mWidth = viewportWidth;
-        mHeight = viewportHeight;
-        mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
-        mCanvas.onViewportInitialized();
-    }
-
-    freeAllSnapshots();
-    mSnapshot = allocSnapshot(&mFirstSnapshot, SaveFlags::MatrixClip);
-    mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
-    mSnapshot->fbo = mCanvas.getTargetFbo();
-    mSnapshot->setRelativeLightCenter(lightCenter);
-    mSaveCount = 1;
-}
-
-Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
-    void* memory;
-    if (mSnapshotPool) {
-        memory = mSnapshotPool;
-        mSnapshotPool = mSnapshotPool->previous;
-        mSnapshotPoolCount--;
-    } else {
-        memory = malloc(sizeof(Snapshot));
-    }
-    return new (memory) Snapshot(previous, savecount);
-}
-
-void CanvasState::freeSnapshot(Snapshot* snapshot) {
-    snapshot->~Snapshot();
-    // Arbitrary number, just don't let this grown unbounded
-    if (mSnapshotPoolCount > 10) {
-        free((void*)snapshot);
-    } else {
-        snapshot->previous = mSnapshotPool;
-        mSnapshotPool = snapshot;
-        mSnapshotPoolCount++;
-    }
-}
-
-void CanvasState::freeAllSnapshots() {
-    while (mSnapshot != &mFirstSnapshot) {
-        Snapshot* temp = mSnapshot;
-        mSnapshot = mSnapshot->previous;
-        freeSnapshot(temp);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Save (layer)
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Guaranteed to save without side-effects
- *
- * This approach, here and in restoreSnapshot(), allows subclasses to directly manipulate the save
- * stack, and ensures restoreToCount() doesn't call back into subclass overrides.
- */
-int CanvasState::saveSnapshot(int flags) {
-    mSnapshot = allocSnapshot(mSnapshot, flags);
-    return mSaveCount++;
-}
-
-int CanvasState::save(int flags) {
-    return saveSnapshot(flags);
-}
-
-/**
- * Guaranteed to restore without side-effects.
- */
-void CanvasState::restoreSnapshot() {
-    Snapshot* toRemove = mSnapshot;
-    Snapshot* toRestore = mSnapshot->previous;
-
-    mSaveCount--;
-    mSnapshot = toRestore;
-
-    // subclass handles restore implementation
-    mCanvas.onSnapshotRestored(*toRemove, *toRestore);
-
-    freeSnapshot(toRemove);
-}
-
-void CanvasState::restore() {
-    if (mSaveCount > 1) {
-        restoreSnapshot();
-    }
-}
-
-void CanvasState::restoreToCount(int saveCount) {
-    if (saveCount < 1) saveCount = 1;
-
-    while (mSaveCount > saveCount) {
-        restoreSnapshot();
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Matrix
-///////////////////////////////////////////////////////////////////////////////
-
-void CanvasState::getMatrix(SkMatrix* matrix) const {
-    mSnapshot->transform->copyTo(*matrix);
-}
-
-void CanvasState::translate(float dx, float dy, float dz) {
-    mSnapshot->transform->translate(dx, dy, dz);
-}
-
-void CanvasState::rotate(float degrees) {
-    mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
-}
-
-void CanvasState::scale(float sx, float sy) {
-    mSnapshot->transform->scale(sx, sy, 1.0f);
-}
-
-void CanvasState::skew(float sx, float sy) {
-    mSnapshot->transform->skew(sx, sy);
-}
-
-void CanvasState::setMatrix(const SkMatrix& matrix) {
-    mSnapshot->transform->load(matrix);
-}
-
-void CanvasState::setMatrix(const Matrix4& matrix) {
-    *(mSnapshot->transform) = matrix;
-}
-
-void CanvasState::concatMatrix(const SkMatrix& matrix) {
-    mat4 transform(matrix);
-    mSnapshot->transform->multiply(transform);
-}
-
-void CanvasState::concatMatrix(const Matrix4& matrix) {
-    mSnapshot->transform->multiply(matrix);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clip
-///////////////////////////////////////////////////////////////////////////////
-
-bool CanvasState::clipRect(float left, float top, float right, float bottom, SkClipOp op) {
-    mSnapshot->clip(Rect(left, top, right, bottom), op);
-    return !mSnapshot->clipIsEmpty();
-}
-
-bool CanvasState::clipPath(const SkPath* path, SkClipOp op) {
-    mSnapshot->clipPath(*path, op);
-    return !mSnapshot->clipIsEmpty();
-}
-
-void CanvasState::setClippingOutline(LinearAllocator& allocator, const Outline* outline) {
-    Rect bounds;
-    float radius;
-    if (!outline->getAsRoundRect(&bounds, &radius)) return;  // only RR supported
-
-    bool outlineIsRounded = MathUtils::isPositive(radius);
-    if (!outlineIsRounded || currentTransform()->isSimple()) {
-        // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
-        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
-    }
-    if (outlineIsRounded) {
-        setClippingRoundRect(allocator, bounds, radius, false);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Quick Rejection
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Calculates whether content drawn within the passed bounds would be outside of, or intersect with
- * the clipRect. Does not modify the scissor.
- *
- * @param clipRequired if not null, will be set to true if element intersects clip
- *         (and wasn't rejected)
- *
- * @param snapOut if set, the geometry will be treated as having an AA ramp.
- *         See Rect::snapGeometryToPixelBoundaries()
- */
-bool CanvasState::calculateQuickRejectForScissor(float left, float top, float right, float bottom,
-                                                 bool* clipRequired, bool* roundRectClipRequired,
-                                                 bool snapOut) const {
-    if (bottom <= top || right <= left) {
-        return true;
-    }
-
-    Rect r(left, top, right, bottom);
-    currentTransform()->mapRect(r);
-    r.snapGeometryToPixelBoundaries(snapOut);
-
-    Rect clipRect(currentRenderTargetClip());
-    clipRect.snapToPixelBoundaries();
-
-    if (!clipRect.intersects(r)) return true;
-
-    // clip is required if geometry intersects clip rect
-    if (clipRequired) {
-        *clipRequired = !clipRect.contains(r);
-    }
-
-    // round rect clip is required if RR clip exists, and geometry intersects its corners
-    if (roundRectClipRequired) {
-        *roundRectClipRequired = mSnapshot->roundRectClipState != nullptr &&
-                                 mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r);
-    }
-    return false;
-}
-
-bool CanvasState::quickRejectConservative(float left, float top, float right, float bottom) const {
-    if (bottom <= top || right <= left) {
-        return true;
-    }
-
-    Rect r(left, top, right, bottom);
-    currentTransform()->mapRect(r);
-    r.roundOut();  // rounded out to be conservative
-
-    Rect clipRect(currentRenderTargetClip());
-    clipRect.snapToPixelBoundaries();
-
-    if (!clipRect.intersects(r)) return true;
-
-    return false;
-}
-
-}  // namespace uirenderer
-}  // namespace android
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
deleted file mode 100644
index 9ac35ff..0000000
--- a/libs/hwui/CanvasState.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "Snapshot.h"
-
-#include <SkClipOp.h>
-#include <SkMatrix.h>
-#include <SkPath.h>
-#include <SkRegion.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Abstract base class for any class containing CanvasState.
- * Defines three mandatory callbacks.
- */
-class CanvasStateClient {
-public:
-    CanvasStateClient() {}
-    virtual ~CanvasStateClient() {}
-
-    /**
-     * Callback allowing embedder to take actions in the middle of a
-     * setViewport() call.
-     */
-    virtual void onViewportInitialized() = 0;
-
-    /**
-     * Callback allowing embedder to take actions in the middle of a
-     * restore() call.  May be called several times sequentially.
-     */
-    virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) = 0;
-
-    /**
-     * Allows subclasses to control what value is stored in snapshot's
-     * fbo field in * initializeSaveStack.
-     */
-    virtual GLuint getTargetFbo() const = 0;
-
-};  // class CanvasStateClient
-
-/**
- * Implements Canvas state methods on behalf of Renderers.
- *
- * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the
- * Renderer interface. Drawing and recording classes that include a CanvasState will have
- * different use cases:
- *
- * Drawing code maintaining canvas state (e.g. FrameBuilder) can query attributes (such as
- * transform) or hook into changes (e.g. save/restore) with minimal surface area for manipulating
- * the stack itself.
- *
- * Recording code maintaining canvas state (e.g. RecordingCanvas) can both record and pass
- * through state operations to CanvasState, so that not only will querying operations work
- * (getClip/Matrix), but so that quickRejection can also be used.
- */
-
-class CanvasState {
-public:
-    explicit CanvasState(CanvasStateClient& renderer);
-    ~CanvasState();
-
-    /**
-     * Initializes the first snapshot, computing the projection matrix,
-     * and stores the dimensions of the render target.
-     */
-    void initializeRecordingSaveStack(int viewportWidth, int viewportHeight);
-
-    /**
-     * Initializes the first snapshot, computing the projection matrix,
-     * and stores the dimensions of the render target.
-     */
-    void initializeSaveStack(int viewportWidth, int viewportHeight, float clipLeft, float clipTop,
-                             float clipRight, float clipBottom, const Vector3& lightCenter);
-
-    bool hasRectToRectTransform() const { return CC_LIKELY(currentTransform()->rectToRect()); }
-
-    // Save (layer)
-    int getSaveCount() const { return mSaveCount; }
-    int save(int flags);
-    void restore();
-    void restoreToCount(int saveCount);
-
-    // Save/Restore without side-effects
-    int saveSnapshot(int flags);
-    void restoreSnapshot();
-
-    // Matrix
-    void getMatrix(SkMatrix* outMatrix) const;
-    void translate(float dx, float dy, float dz = 0.0f);
-    void rotate(float degrees);
-    void scale(float sx, float sy);
-    void skew(float sx, float sy);
-
-    void setMatrix(const SkMatrix& matrix);
-    void setMatrix(const Matrix4& matrix);  // internal only convenience method
-    void concatMatrix(const SkMatrix& matrix);
-    void concatMatrix(const Matrix4& matrix);  // internal only convenience method
-
-    // Clip
-    const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
-    const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
-
-    bool quickRejectConservative(float left, float top, float right, float bottom) const;
-
-    bool clipRect(float left, float top, float right, float bottom, SkClipOp op);
-    bool clipPath(const SkPath* path, SkClipOp op);
-
-    /**
-     * Sets a "clipping outline", which is independent from the regular clip.
-     * Currently only supports rectangles or rounded rectangles; passing in a
-     * more complicated outline fails silently. Replaces any previous clipping
-     * outline.
-     */
-    void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
-    void setClippingRoundRect(LinearAllocator& allocator, const Rect& rect, float radius,
-                              bool highPriority = true) {
-        mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
-    }
-    void setProjectionPathMask(const SkPath* path) { mSnapshot->setProjectionPathMask(path); }
-
-    /**
-     * Returns true if drawing in the rectangle (left, top, right, bottom)
-     * will be clipped out. Is conservative: might return false when subpixel-
-     * perfect tests would return true.
-     */
-    bool calculateQuickRejectForScissor(float left, float top, float right, float bottom,
-                                        bool* clipRequired, bool* roundRectClipRequired,
-                                        bool snapOut) const;
-
-    void scaleAlpha(float alpha) { mSnapshot->alpha *= alpha; }
-
-    inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
-    inline const Rect& currentRenderTargetClip() const {
-        return currentSnapshot()->getRenderTargetClip();
-    }
-    inline int currentFlags() const { return currentSnapshot()->flags; }
-    const Vector3& currentLightCenter() const {
-        return currentSnapshot()->getRelativeLightCenter();
-    }
-    int getViewportWidth() const { return currentSnapshot()->getViewportWidth(); }
-    int getViewportHeight() const { return currentSnapshot()->getViewportHeight(); }
-    int getWidth() const { return mWidth; }
-    int getHeight() const { return mHeight; }
-    bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
-
-    inline const Snapshot* currentSnapshot() const { return mSnapshot; }
-    inline Snapshot* writableSnapshot() { return mSnapshot; }
-    inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
-
-private:
-    Snapshot* allocSnapshot(Snapshot* previous, int savecount);
-    void freeSnapshot(Snapshot* snapshot);
-    void freeAllSnapshots();
-
-    /// Dimensions of the drawing surface
-    int mWidth, mHeight;
-
-    /// Number of saved states
-    int mSaveCount;
-
-    /// Base state
-    Snapshot mFirstSnapshot;
-
-    /// Host providing callbacks
-    CanvasStateClient& mCanvas;
-
-    /// Current state
-    Snapshot* mSnapshot;
-
-    // Pool of allocated snapshots to re-use
-    // NOTE: The dtors have already been invoked!
-    Snapshot* mSnapshotPool = nullptr;
-    int mSnapshotPoolCount = 0;
-
-};  // class CanvasState
-
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
deleted file mode 100644
index 27d93cf..0000000
--- a/libs/hwui/ClipArea.cpp
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "ClipArea.h"
-
-#include "utils/LinearAllocator.h"
-
-#include <SkPath.h>
-#include <limits>
-#include <type_traits>
-
-namespace android {
-namespace uirenderer {
-
-static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
-    Vertex v = {x, y};
-    transform.mapPoint(v.x, v.y);
-    transformedBounds.expandToCover(v.x, v.y);
-}
-
-Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
-    const float kMinFloat = std::numeric_limits<float>::lowest();
-    const float kMaxFloat = std::numeric_limits<float>::max();
-    Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat};
-    handlePoint(transformedBounds, transform, r.left, r.top);
-    handlePoint(transformedBounds, transform, r.right, r.top);
-    handlePoint(transformedBounds, transform, r.left, r.bottom);
-    handlePoint(transformedBounds, transform, r.right, r.bottom);
-    return transformedBounds;
-}
-
-void ClipBase::dump() const {
-    ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect));
-}
-
-/*
- * TransformedRectangle
- */
-
-TransformedRectangle::TransformedRectangle() {}
-
-TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform)
-        : mBounds(bounds), mTransform(transform) {}
-
-bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const {
-    return mTransform == other.mTransform;
-}
-
-void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
-    mBounds.doIntersect(other.mBounds);
-}
-
-bool TransformedRectangle::isEmpty() const {
-    return mBounds.isEmpty();
-}
-
-/*
- * RectangleList
- */
-
-RectangleList::RectangleList() : mTransformedRectanglesCount(0) {}
-
-bool RectangleList::isEmpty() const {
-    if (mTransformedRectanglesCount < 1) {
-        return true;
-    }
-
-    for (int i = 0; i < mTransformedRectanglesCount; i++) {
-        if (mTransformedRectangles[i].isEmpty()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-int RectangleList::getTransformedRectanglesCount() const {
-    return mTransformedRectanglesCount;
-}
-
-const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
-    return mTransformedRectangles[i];
-}
-
-void RectangleList::setEmpty() {
-    mTransformedRectanglesCount = 0;
-}
-
-void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
-    mTransformedRectanglesCount = 1;
-    mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
-}
-
-bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) {
-    TransformedRectangle newRectangle(bounds, transform);
-
-    // Try to find a rectangle with a compatible transformation
-    int index = 0;
-    for (; index < mTransformedRectanglesCount; index++) {
-        TransformedRectangle& tr(mTransformedRectangles[index]);
-        if (tr.canSimplyIntersectWith(newRectangle)) {
-            tr.intersectWith(newRectangle);
-            return true;
-        }
-    }
-
-    // Add it to the list if there is room
-    if (index < kMaxTransformedRectangles) {
-        mTransformedRectangles[index] = newRectangle;
-        mTransformedRectanglesCount += 1;
-        return true;
-    }
-
-    // This rectangle list is full
-    return false;
-}
-
-Rect RectangleList::calculateBounds() const {
-    Rect bounds;
-    for (int index = 0; index < mTransformedRectanglesCount; index++) {
-        const TransformedRectangle& tr(mTransformedRectangles[index]);
-        if (index == 0) {
-            bounds = tr.transformedBounds();
-        } else {
-            bounds.doIntersect(tr.transformedBounds());
-        }
-    }
-    return bounds;
-}
-
-static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) {
-    SkPath rectPath;
-    SkPath rectPathTransformed;
-    rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
-    SkMatrix skTransform;
-    transform.copyTo(skTransform);
-    rectPath.transform(skTransform, &rectPathTransformed);
-    return rectPathTransformed;
-}
-
-SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
-    SkRegion rectangleListAsRegion;
-    for (int index = 0; index < mTransformedRectanglesCount; index++) {
-        const TransformedRectangle& tr(mTransformedRectangles[index]);
-        SkPath rectPathTransformed =
-                pathFromTransformedRectangle(tr.getBounds(), tr.getTransform());
-        if (index == 0) {
-            rectangleListAsRegion.setPath(rectPathTransformed, clip);
-        } else {
-            SkRegion rectRegion;
-            rectRegion.setPath(rectPathTransformed, clip);
-            rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
-        }
-    }
-    return rectangleListAsRegion;
-}
-
-void RectangleList::transform(const Matrix4& transform) {
-    for (int index = 0; index < mTransformedRectanglesCount; index++) {
-        mTransformedRectangles[index].transform(transform);
-    }
-}
-
-/*
- * ClipArea
- */
-
-ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {}
-
-/*
- * Interface
- */
-
-void ClipArea::setViewportDimensions(int width, int height) {
-    mPostViewportClipObserved = false;
-    mViewportBounds.set(0, 0, width, height);
-    mClipRect = mViewportBounds;
-}
-
-void ClipArea::setEmpty() {
-    onClipUpdated();
-    mMode = ClipMode::Rectangle;
-    mClipRect.setEmpty();
-    mClipRegion.setEmpty();
-    mRectangleList.setEmpty();
-}
-
-void ClipArea::setClip(float left, float top, float right, float bottom) {
-    onClipUpdated();
-    mMode = ClipMode::Rectangle;
-    mClipRect.set(left, top, right, bottom);
-    mClipRegion.setEmpty();
-}
-
-void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) {
-    if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
-    if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
-    onClipUpdated();
-    switch (mMode) {
-        case ClipMode::Rectangle:
-            rectangleModeClipRectWithTransform(r, transform, op);
-            break;
-        case ClipMode::RectangleList:
-            rectangleListModeClipRectWithTransform(r, transform, op);
-            break;
-        case ClipMode::Region:
-            regionModeClipRectWithTransform(r, transform, op);
-            break;
-    }
-}
-
-void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
-    if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
-    if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
-    onClipUpdated();
-    enterRegionMode();
-    mClipRegion.op(region, op);
-    onClipRegionUpdated();
-}
-
-void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) {
-    if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
-    if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
-    onClipUpdated();
-    SkMatrix skTransform;
-    transform->copyTo(skTransform);
-    SkPath transformed;
-    path.transform(skTransform, &transformed);
-    SkRegion region;
-    regionFromPath(transformed, region);
-    enterRegionMode();
-    mClipRegion.op(region, op);
-    onClipRegionUpdated();
-}
-
-/*
- * Rectangle mode
- */
-
-void ClipArea::enterRectangleMode() {
-    // Entering rectangle mode discards any
-    // existing clipping information from the other modes.
-    // The only way this occurs is by a clip setting operation.
-    mMode = ClipMode::Rectangle;
-}
-
-void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform,
-                                                  SkRegion::Op op) {
-    if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
-        mClipRect = r;
-        transform->mapRect(mClipRect);
-        return;
-    } else if (op != SkRegion::kIntersect_Op) {
-        enterRegionMode();
-        regionModeClipRectWithTransform(r, transform, op);
-        return;
-    }
-
-    if (transform->rectToRect()) {
-        Rect transformed(r);
-        transform->mapRect(transformed);
-        mClipRect.doIntersect(transformed);
-        return;
-    }
-
-    enterRectangleListMode();
-    rectangleListModeClipRectWithTransform(r, transform, op);
-}
-
-/*
- * RectangleList mode implementation
- */
-
-void ClipArea::enterRectangleListMode() {
-    // Is is only legal to enter rectangle list mode from
-    // rectangle mode, since rectangle list mode cannot represent
-    // all clip areas that can be represented by a region.
-    ALOG_ASSERT(mMode == ClipMode::Rectangle);
-    mMode = ClipMode::RectangleList;
-    mRectangleList.set(mClipRect, Matrix4::identity());
-}
-
-void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
-                                                      SkRegion::Op op) {
-    if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) {
-        enterRegionMode();
-        regionModeClipRectWithTransform(r, transform, op);
-    }
-}
-
-/*
- * Region mode implementation
- */
-
-void ClipArea::enterRegionMode() {
-    ClipMode oldMode = mMode;
-    mMode = ClipMode::Region;
-    if (oldMode != ClipMode::Region) {
-        if (oldMode == ClipMode::Rectangle) {
-            mClipRegion.setRect(mClipRect.toSkIRect());
-        } else {
-            mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
-            onClipRegionUpdated();
-        }
-    }
-}
-
-void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
-                                               SkRegion::Op op) {
-    SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
-    SkRegion transformedRectRegion;
-    regionFromPath(transformedRect, transformedRectRegion);
-    mClipRegion.op(transformedRectRegion, op);
-    onClipRegionUpdated();
-}
-
-void ClipArea::onClipRegionUpdated() {
-    if (!mClipRegion.isEmpty()) {
-        mClipRect.set(mClipRegion.getBounds());
-
-        if (mClipRegion.isRect()) {
-            mClipRegion.setEmpty();
-            enterRectangleMode();
-        }
-    } else {
-        mClipRect.setEmpty();
-    }
-}
-
-/**
- * Clip serialization
- */
-
-const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
-    if (!mPostViewportClipObserved) {
-        // Only initial clip-to-viewport observed, so no serialization of clip necessary
-        return nullptr;
-    }
-
-    static_assert(std::is_trivially_destructible<Rect>::value,
-                  "expect Rect to be trivially destructible");
-    static_assert(std::is_trivially_destructible<RectangleList>::value,
-                  "expect RectangleList to be trivially destructible");
-
-    if (mLastSerialization == nullptr) {
-        ClipBase* serialization = nullptr;
-        switch (mMode) {
-            case ClipMode::Rectangle:
-                serialization = allocator.create<ClipRect>(mClipRect);
-                break;
-            case ClipMode::RectangleList:
-                serialization = allocator.create<ClipRectList>(mRectangleList);
-                serialization->rect = mRectangleList.calculateBounds();
-                break;
-            case ClipMode::Region:
-                serialization = allocator.create<ClipRegion>(mClipRegion);
-                serialization->rect.set(mClipRegion.getBounds());
-                break;
-        }
-        serialization->intersectWithRoot = mReplaceOpObserved;
-        // TODO: this is only done for draw time, should eventually avoid for record time
-        serialization->rect.snapToPixelBoundaries();
-        mLastSerialization = serialization;
-    }
-    return mLastSerialization;
-}
-
-inline static const RectangleList& getRectList(const ClipBase* scb) {
-    return reinterpret_cast<const ClipRectList*>(scb)->rectList;
-}
-
-inline static const SkRegion& getRegion(const ClipBase* scb) {
-    return reinterpret_cast<const ClipRegion*>(scb)->region;
-}
-
-// Conservative check for too many rectangles to fit in rectangle list.
-// For simplicity, doesn't account for rect merging
-static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
-    int currentRectCount = clipArea.isRectangleList()
-                                   ? clipArea.getRectangleList().getTransformedRectanglesCount()
-                                   : 1;
-    int recordedRectCount = (scb->mode == ClipMode::RectangleList)
-                                    ? getRectList(scb).getTransformedRectanglesCount()
-                                    : 1;
-    return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
-}
-
-static const ClipRect sEmptyClipRect(Rect(0, 0));
-
-const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
-                                                   const ClipBase* recordedClip,
-                                                   const Matrix4& recordedClipTransform) {
-    // if no recordedClip passed, just serialize current state
-    if (!recordedClip) return serializeClip(allocator);
-
-    // if either is empty, clip is empty
-    if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect;
-
-    if (!mLastResolutionResult || recordedClip != mLastResolutionClip ||
-        recordedClipTransform != mLastResolutionTransform) {
-        mLastResolutionClip = recordedClip;
-        mLastResolutionTransform = recordedClipTransform;
-
-        if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle &&
-                      recordedClipTransform.rectToRect())) {
-            // common case - result is a single rectangle
-            auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
-            recordedClipTransform.mapRect(rectClip->rect);
-            rectClip->rect.doIntersect(mClipRect);
-            rectClip->rect.snapToPixelBoundaries();
-            mLastResolutionResult = rectClip;
-        } else if (CC_UNLIKELY(mMode == ClipMode::Region ||
-                               recordedClip->mode == ClipMode::Region ||
-                               cannotFitInRectangleList(*this, recordedClip))) {
-            // region case
-            SkRegion other;
-            switch (recordedClip->mode) {
-                case ClipMode::Rectangle:
-                    if (CC_LIKELY(recordedClipTransform.rectToRect())) {
-                        // simple transform, skip creating SkPath
-                        Rect resultClip(recordedClip->rect);
-                        recordedClipTransform.mapRect(resultClip);
-                        other.setRect(resultClip.toSkIRect());
-                    } else {
-                        SkPath transformedRect = pathFromTransformedRectangle(
-                                recordedClip->rect, recordedClipTransform);
-                        other.setPath(transformedRect, createViewportRegion());
-                    }
-                    break;
-                case ClipMode::RectangleList: {
-                    RectangleList transformedList(getRectList(recordedClip));
-                    transformedList.transform(recordedClipTransform);
-                    other = transformedList.convertToRegion(createViewportRegion());
-                    break;
-                }
-                case ClipMode::Region:
-                    other = getRegion(recordedClip);
-                    applyTransformToRegion(recordedClipTransform, &other);
-            }
-
-            ClipRegion* regionClip = allocator.create<ClipRegion>();
-            switch (mMode) {
-                case ClipMode::Rectangle:
-                    regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
-                    break;
-                case ClipMode::RectangleList:
-                    regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
-                                          other, SkRegion::kIntersect_Op);
-                    break;
-                case ClipMode::Region:
-                    regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
-                    break;
-            }
-            // Don't need to snap, since region's in int bounds
-            regionClip->rect.set(regionClip->region.getBounds());
-            mLastResolutionResult = regionClip;
-        } else {
-            auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
-            auto&& rectList = rectListClip->rectList;
-            if (mMode == ClipMode::Rectangle) {
-                rectList.set(mClipRect, Matrix4::identity());
-            }
-
-            if (recordedClip->mode == ClipMode::Rectangle) {
-                rectList.intersectWith(recordedClip->rect, recordedClipTransform);
-            } else {
-                const RectangleList& other = getRectList(recordedClip);
-                for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
-                    auto&& tr = other.getTransformedRectangle(i);
-                    Matrix4 totalTransform(recordedClipTransform);
-                    totalTransform.multiply(tr.getTransform());
-                    rectList.intersectWith(tr.getBounds(), totalTransform);
-                }
-            }
-            rectListClip->rect = rectList.calculateBounds();
-            rectListClip->rect.snapToPixelBoundaries();
-            mLastResolutionResult = rectListClip;
-        }
-    }
-    return mLastResolutionResult;
-}
-
-void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
-    if (!clip) return;  // nothing to do
-
-    if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
-        clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
-    } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
-        auto&& rectList = getRectList(clip);
-        for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
-            auto&& tr = rectList.getTransformedRectangle(i);
-            Matrix4 totalTransform(transform);
-            totalTransform.multiply(tr.getTransform());
-            clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
-        }
-    } else {
-        SkRegion region(getRegion(clip));
-        applyTransformToRegion(transform, &region);
-        clipRegion(region, SkRegion::kIntersect_Op);
-    }
-}
-
-void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) {
-    if (transform.rectToRect() && !transform.isPureTranslate()) {
-        // handle matrices with scale manually by mapping each rect
-        SkRegion other;
-        SkRegion::Iterator it(*region);
-        while (!it.done()) {
-            Rect rect(it.rect());
-            transform.mapRect(rect);
-            rect.snapGeometryToPixelBoundaries(true);
-            other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op);
-            it.next();
-        }
-        region->swap(other);
-    } else {
-        // TODO: handle non-translate transforms properly!
-        region->translate(transform.getTranslateX(), transform.getTranslateY());
-    }
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
deleted file mode 100644
index a7a1180..0000000
--- a/libs/hwui/ClipArea.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef CLIPAREA_H
-#define CLIPAREA_H
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "utils/Pair.h"
-
-#include <SkRegion.h>
-
-namespace android {
-namespace uirenderer {
-
-class LinearAllocator;
-
-Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
-
-class TransformedRectangle {
-public:
-    TransformedRectangle();
-    TransformedRectangle(const Rect& bounds, const Matrix4& transform);
-
-    bool canSimplyIntersectWith(const TransformedRectangle& other) const;
-    void intersectWith(const TransformedRectangle& other);
-
-    bool isEmpty() const;
-
-    const Rect& getBounds() const { return mBounds; }
-
-    Rect transformedBounds() const {
-        Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
-        return transformedBounds;
-    }
-
-    const Matrix4& getTransform() const { return mTransform; }
-
-    void transform(const Matrix4& transform) {
-        Matrix4 t;
-        t.loadMultiply(transform, mTransform);
-        mTransform = t;
-    }
-
-private:
-    Rect mBounds;
-    Matrix4 mTransform;
-};
-
-class RectangleList {
-public:
-    RectangleList();
-
-    bool isEmpty() const;
-    int getTransformedRectanglesCount() const;
-    const TransformedRectangle& getTransformedRectangle(int i) const;
-
-    void setEmpty();
-    void set(const Rect& bounds, const Matrix4& transform);
-    bool intersectWith(const Rect& bounds, const Matrix4& transform);
-    void transform(const Matrix4& transform);
-
-    SkRegion convertToRegion(const SkRegion& clip) const;
-    Rect calculateBounds() const;
-
-    enum { kMaxTransformedRectangles = 5 };
-
-private:
-    int mTransformedRectanglesCount;
-    TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
-};
-
-enum class ClipMode {
-    Rectangle,
-    RectangleList,
-
-    // region and path - intersected. if either is empty, don't use
-    Region
-};
-
-struct ClipBase {
-    explicit ClipBase(ClipMode mode) : mode(mode) {}
-    explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {}
-    const ClipMode mode;
-    bool intersectWithRoot = false;
-    // Bounds of the clipping area, used to define the scissor, and define which
-    // portion of the stencil is updated/used
-    Rect rect;
-
-    void dump() const;
-};
-
-struct ClipRect : ClipBase {
-    explicit ClipRect(const Rect& rect) : ClipBase(rect) {}
-};
-
-struct ClipRectList : ClipBase {
-    explicit ClipRectList(const RectangleList& rectList)
-            : ClipBase(ClipMode::RectangleList), rectList(rectList) {}
-    RectangleList rectList;
-};
-
-struct ClipRegion : ClipBase {
-    explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {}
-    ClipRegion() : ClipBase(ClipMode::Region) {}
-    SkRegion region;
-};
-
-class ClipArea {
-public:
-    ClipArea();
-
-    void setViewportDimensions(int width, int height);
-
-    bool isEmpty() const { return mClipRect.isEmpty(); }
-
-    void setEmpty();
-    void setClip(float left, float top, float right, float bottom);
-    void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-    void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op);
-
-    const Rect& getClipRect() const { return mClipRect; }
-
-    const SkRegion& getClipRegion() const { return mClipRegion; }
-
-    const RectangleList& getRectangleList() const { return mRectangleList; }
-
-    bool isRegion() const { return ClipMode::Region == mMode; }
-
-    bool isSimple() const { return mMode == ClipMode::Rectangle; }
-
-    bool isRectangleList() const { return mMode == ClipMode::RectangleList; }
-
-    WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
-    WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
-            LinearAllocator& allocator, const ClipBase* recordedClip,
-            const Matrix4& recordedClipTransform);
-    void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
-
-    static void applyTransformToRegion(const Matrix4& transform, SkRegion* region);
-
-private:
-    void enterRectangleMode();
-    void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-
-    void enterRectangleListMode();
-    void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
-                                                SkRegion::Op op);
-
-    void enterRegionModeFromRectangleMode();
-    void enterRegionModeFromRectangleListMode();
-    void enterRegionMode();
-    void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
-
-    void clipRegion(const SkRegion& region, SkRegion::Op op);
-    void ensureClipRegion();
-    void onClipRegionUpdated();
-
-    // Called by every state modifying public method.
-    void onClipUpdated() {
-        mPostViewportClipObserved = true;
-        mLastSerialization = nullptr;
-        mLastResolutionResult = nullptr;
-    }
-
-    SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); }
-
-    void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
-        // TODO: this should not mask every path to the viewport - this makes it impossible to use
-        // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
-        pathAsRegion.setPath(path, createViewportRegion());
-    }
-
-    ClipMode mMode;
-    bool mPostViewportClipObserved = false;
-    bool mReplaceOpObserved = false;
-
-    /**
-     * If mLastSerialization is non-null, it represents an already serialized copy
-     * of the current clip state. If null, it has not been computed.
-     */
-    const ClipBase* mLastSerialization = nullptr;
-
-    /**
-     * This pair of pointers is a single entry cache of most recently seen
-     */
-    const ClipBase* mLastResolutionResult = nullptr;
-    const ClipBase* mLastResolutionClip = nullptr;
-    Matrix4 mLastResolutionTransform;
-
-    Rect mViewportBounds;
-    Rect mClipRect;
-    SkRegion mClipRegion;
-    RectangleList mRectangleList;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* CLIPAREA_H_ */
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
deleted file mode 100644
index b424f97..0000000
--- a/libs/hwui/FloatColor.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef FLOATCOLOR_H
-#define FLOATCOLOR_H
-
-#include "utils/Color.h"
-#include "utils/Macros.h"
-#include "utils/MathUtils.h"
-
-#include <stdint.h>
-
-namespace android {
-namespace uirenderer {
-
-struct FloatColor {
-    // "color" is a gamma-encoded sRGB color
-    // After calling this method, the color is stored as a pre-multiplied linear color
-    // if linear blending is enabled. Otherwise, the color is stored as a pre-multiplied
-    // gamma-encoded sRGB color
-    void set(uint32_t color) {
-        a = ((color >> 24) & 0xff) / 255.0f;
-        r = a * EOCF(((color >> 16) & 0xff) / 255.0f);
-        g = a * EOCF(((color >> 8) & 0xff) / 255.0f);
-        b = a * EOCF(((color)&0xff) / 255.0f);
-    }
-
-    // "color" is a gamma-encoded sRGB color
-    // After calling this method, the color is stored as a un-premultiplied linear color
-    // if linear blending is enabled. Otherwise, the color is stored as a un-premultiplied
-    // gamma-encoded sRGB color
-    void setUnPreMultiplied(uint32_t color) {
-        a = ((color >> 24) & 0xff) / 255.0f;
-        r = EOCF(((color >> 16) & 0xff) / 255.0f);
-        g = EOCF(((color >> 8) & 0xff) / 255.0f);
-        b = EOCF(((color)&0xff) / 255.0f);
-    }
-
-    bool isNotBlack() { return a < 1.0f || r > 0.0f || g > 0.0f || b > 0.0f; }
-
-    bool operator==(const FloatColor& other) const {
-        return MathUtils::areEqual(r, other.r) && MathUtils::areEqual(g, other.g) &&
-               MathUtils::areEqual(b, other.b) && MathUtils::areEqual(a, other.a);
-    }
-
-    bool operator!=(const FloatColor& other) const { return !(*this == other); }
-
-    float r;
-    float g;
-    float b;
-    float a;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FloatColor);
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* FLOATCOLOR_H */
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
deleted file mode 100644
index 65bee47..0000000
--- a/libs/hwui/ResourceCache.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2010 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 "ResourceCache.h"
-
-namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
-
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Resource cache
-///////////////////////////////////////////////////////////////////////////////
-
-void ResourceCache::logCache() {
-    ALOGD("ResourceCache: cacheReport:");
-    for (size_t i = 0; i < mCache->size(); ++i) {
-        ResourceReference* ref = mCache->valueAt(i);
-        ALOGD("  ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p", i, mCache->keyAt(i),
-              mCache->valueAt(i));
-        ALOGD("  ResourceCache: mCache(%zu): refCount, destroyed, type = %d, %d, %d", i,
-              ref->refCount, ref->destroyed, ref->resourceType);
-    }
-}
-
-ResourceCache::ResourceCache() {
-    Mutex::Autolock _l(mLock);
-    mCache = new KeyedVector<const void*, ResourceReference*>();
-}
-
-ResourceCache::~ResourceCache() {
-    Mutex::Autolock _l(mLock);
-    delete mCache;
-}
-
-void ResourceCache::lock() {
-    mLock.lock();
-}
-
-void ResourceCache::unlock() {
-    mLock.unlock();
-}
-
-void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
-    Mutex::Autolock _l(mLock);
-    incrementRefcountLocked(resource, resourceType);
-}
-
-void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
-    incrementRefcount((void*)patchResource, kNinePatch);
-}
-
-void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
-    ssize_t index = mCache->indexOfKey(resource);
-    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
-    if (ref == nullptr || mCache->size() == 0) {
-        ref = new ResourceReference(resourceType);
-        mCache->add(resource, ref);
-    }
-    ref->refCount++;
-}
-
-void ResourceCache::decrementRefcount(void* resource) {
-    Mutex::Autolock _l(mLock);
-    decrementRefcountLocked(resource);
-}
-
-void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
-    decrementRefcount((void*)patchResource);
-}
-
-void ResourceCache::decrementRefcountLocked(void* resource) {
-    ssize_t index = mCache->indexOfKey(resource);
-    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
-    if (ref == nullptr) {
-        // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
-        return;
-    }
-    ref->refCount--;
-    if (ref->refCount == 0) {
-        deleteResourceReferenceLocked(resource, ref);
-    }
-}
-
-void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
-    decrementRefcountLocked((void*)patchResource);
-}
-
-void ResourceCache::destructor(Res_png_9patch* resource) {
-    Mutex::Autolock _l(mLock);
-    destructorLocked(resource);
-}
-
-void ResourceCache::destructorLocked(Res_png_9patch* resource) {
-    ssize_t index = mCache->indexOfKey(resource);
-    ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
-    if (ref == nullptr) {
-        // If we're not tracking this resource, just delete it
-        // A Res_png_9patch is actually an array of byte that's larger
-        // than sizeof(Res_png_9patch). It must be freed as an array.
-        delete[](int8_t*) resource;
-        return;
-    }
-    ref->destroyed = true;
-    if (ref->refCount == 0) {
-        deleteResourceReferenceLocked(resource, ref);
-    }
-}
-
-/**
- * This method should only be called while the mLock mutex is held (that mutex is grabbed
- * by the various destructor() and recycle() methods which call this method).
- */
-void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
-    if (ref->destroyed) {
-        switch (ref->resourceType) {
-            case kNinePatch: {
-                // A Res_png_9patch is actually an array of byte that's larger
-                // than sizeof(Res_png_9patch). It must be freed as an array.
-                int8_t* patch = (int8_t*)resource;
-                delete[] patch;
-            } break;
-        }
-    }
-    mCache->removeItem(resource);
-    delete ref;
-}
-
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
deleted file mode 100644
index fd3f9fd..0000000
--- a/libs/hwui/ResourceCache.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2010 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 ANDROID_HWUI_RESOURCE_CACHE_H
-#define ANDROID_HWUI_RESOURCE_CACHE_H
-
-#include <cutils/compiler.h>
-
-#include <SkBitmap.h>
-#include <SkPixelRef.h>
-
-#include <utils/KeyedVector.h>
-#include <utils/Singleton.h>
-
-#include <androidfw/ResourceTypes.h>
-
-namespace android {
-namespace uirenderer {
-
-class Layer;
-
-/**
- * Type of Resource being cached
- */
-enum ResourceType {
-    kNinePatch,
-};
-
-class ResourceReference {
-public:
-    explicit ResourceReference(ResourceType type) {
-        refCount = 0;
-        destroyed = false;
-        resourceType = type;
-    }
-
-    int refCount;
-    bool destroyed;
-    ResourceType resourceType;
-};
-
-class ANDROID_API ResourceCache : public Singleton<ResourceCache> {
-    ResourceCache();
-    ~ResourceCache();
-
-    friend class Singleton<ResourceCache>;
-
-public:
-    /**
-     * When using these two methods, make sure to only invoke the *Locked()
-     * variants of increment/decrementRefcount(), recyle() and destructor()
-     */
-    void lock();
-    void unlock();
-
-    void incrementRefcount(const Res_png_9patch* resource);
-
-    void decrementRefcount(const Res_png_9patch* resource);
-
-    void decrementRefcountLocked(const Res_png_9patch* resource);
-
-    void destructor(Res_png_9patch* resource);
-
-    void destructorLocked(Res_png_9patch* resource);
-
-private:
-    void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref);
-
-    void incrementRefcount(void* resource, ResourceType resourceType);
-    void incrementRefcountLocked(void* resource, ResourceType resourceType);
-
-    void decrementRefcount(void* resource);
-    void decrementRefcountLocked(void* resource);
-
-    void logCache();
-
-    /**
-     * Used to increment, decrement, and destroy. Incrementing is generally accessed on the UI
-     * thread, but destroying resources may be called from the GC thread, the finalizer thread,
-     * or a reference queue finalization thread.
-     */
-    mutable Mutex mLock;
-
-    KeyedVector<const void*, ResourceReference*>* mCache;
-};
-
-};  // namespace uirenderer
-};  // namespace android
-
-#endif  // ANDROID_HWUI_RESOURCE_CACHE_H
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
deleted file mode 100644
index f1a1bef..0000000
--- a/libs/hwui/Snapshot.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2012 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 "Snapshot.h"
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors
-///////////////////////////////////////////////////////////////////////////////
-
-Snapshot::Snapshot()
-        : flags(0)
-        , previous(nullptr)
-        , layer(nullptr)
-        , fbo(0)
-        , alpha(1.0f)
-        , roundRectClipState(nullptr)
-        , projectionPathMask(nullptr)
-        , mClipArea(&mClipAreaRoot) {
-    transform = &mTransformRoot;
-    mRelativeLightCenter.x = mRelativeLightCenter.y = mRelativeLightCenter.z = 0;
-}
-
-/**
- * Copies the specified snapshot/ The specified snapshot is stored as
- * the previous snapshot.
- */
-Snapshot::Snapshot(Snapshot* s, int saveFlags)
-        : flags(0)
-        , previous(s)
-        , layer(s->layer)
-        , fbo(s->fbo)
-        , alpha(s->alpha)
-        , roundRectClipState(s->roundRectClipState)
-        , projectionPathMask(s->projectionPathMask)
-        , mClipArea(nullptr)
-        , mViewportData(s->mViewportData)
-        , mRelativeLightCenter(s->mRelativeLightCenter) {
-    if (saveFlags & SaveFlags::Matrix) {
-        mTransformRoot = *s->transform;
-        transform = &mTransformRoot;
-    } else {
-        transform = s->transform;
-    }
-
-    if (saveFlags & SaveFlags::Clip) {
-        mClipAreaRoot = s->getClipArea();
-        mClipArea = &mClipAreaRoot;
-    } else {
-        mClipArea = s->mClipArea;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::clip(const Rect& localClip, SkClipOp op) {
-    flags |= Snapshot::kFlagClipSet;
-    mClipArea->clipRectWithTransform(localClip, transform, static_cast<SkRegion::Op>(op));
-}
-
-void Snapshot::clipPath(const SkPath& path, SkClipOp op) {
-    flags |= Snapshot::kFlagClipSet;
-    mClipArea->clipPathWithTransform(path, transform, static_cast<SkRegion::Op>(op));
-}
-
-void Snapshot::setClip(float left, float top, float right, float bottom) {
-    flags |= Snapshot::kFlagClipSet;
-    mClipArea->setClip(left, top, right, bottom);
-}
-
-bool Snapshot::hasPerspectiveTransform() const {
-    return transform->isPerspective();
-}
-
-const Rect& Snapshot::getLocalClip() {
-    mat4 inverse;
-    inverse.loadInverse(*transform);
-
-    mLocalClip.set(mClipArea->getClipRect());
-    inverse.mapRect(mLocalClip);
-
-    return mLocalClip;
-}
-
-void Snapshot::resetClip(float left, float top, float right, float bottom) {
-    // TODO: This is incorrect, when we start rendering into a new layer,
-    // we may have to modify the previous snapshot's clip rect and clip
-    // region if the previous restore() call did not restore the clip
-    mClipArea = &mClipAreaRoot;
-    setClip(left, top, right, bottom);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Clipping round rect
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius,
-                                    bool highPriority) {
-    if (bounds.isEmpty()) {
-        mClipArea->setEmpty();
-        return;
-    }
-
-    if (roundRectClipState && roundRectClipState->highPriority) {
-        // ignore, don't replace, already have a high priority clip
-        return;
-    }
-
-    RoundRectClipState* state = new (allocator) RoundRectClipState;
-
-    state->highPriority = highPriority;
-
-    // store the inverse drawing matrix
-    Matrix4 roundRectDrawingMatrix = getOrthoMatrix();
-    roundRectDrawingMatrix.multiply(*transform);
-    state->matrix.loadInverse(roundRectDrawingMatrix);
-
-    // compute area under rounded corners - only draws overlapping these rects need to be clipped
-    for (int i = 0; i < 4; i++) {
-        state->dangerRects[i] = bounds;
-    }
-    state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius;
-    state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius;
-    state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius;
-    state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius;
-    for (int i = 0; i < 4; i++) {
-        transform->mapRect(state->dangerRects[i]);
-
-        // round danger rects out as though they are AA geometry (since they essentially are)
-        state->dangerRects[i].snapGeometryToPixelBoundaries(true);
-    }
-
-    // store RR area
-    state->innerRect = bounds;
-    state->innerRect.inset(radius);
-    state->radius = radius;
-
-    // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info
-    roundRectClipState = state;
-}
-
-void Snapshot::setProjectionPathMask(const SkPath* path) {
-    projectionPathMask = path;
-}
-
-static Snapshot* getClipRoot(Snapshot* target) {
-    while (target->previous && target->previous->previous) {
-        target = target->previous;
-    }
-    return target;
-}
-
-const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator,
-                                                   const ClipBase* recordedClip,
-                                                   const Matrix4& recordedClipTransform) {
-    auto target = this;
-    if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
-        // Clip must be intersected with root, instead of current clip.
-        target = getClipRoot(this);
-    }
-
-    return target->mClipArea->serializeIntersectedClip(allocator, recordedClip,
-                                                       recordedClipTransform);
-}
-
-void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) {
-    if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
-        // current clip is being replaced, but must intersect with clip root
-        *mClipArea = *(getClipRoot(this)->mClipArea);
-    }
-    mClipArea->applyClip(recordedClip, transform);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Queries
-///////////////////////////////////////////////////////////////////////////////
-
-void Snapshot::dump() const {
-    ALOGD("Snapshot %p, flags %x, prev %p, height %d, hasComplexClip %d", this, flags, previous,
-          getViewportHeight(), !mClipArea->isSimple());
-    const Rect& clipRect(mClipArea->getClipRect());
-    ALOGD("  ClipRect %.1f %.1f %.1f %.1f, clip simple %d", clipRect.left, clipRect.top,
-          clipRect.right, clipRect.bottom, mClipArea->isSimple());
-
-    ALOGD("  Transform (at %p):", transform);
-    transform->dump();
-}
-
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
deleted file mode 100644
index 655f819..0000000
--- a/libs/hwui/Snapshot.h
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#pragma once
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/Region.h>
-#include <utils/LinearAllocator.h>
-#include <utils/RefBase.h>
-
-#include <SkClipOp.h>
-#include <SkRegion.h>
-
-#include "ClipArea.h"
-#include "Layer.h"
-#include "Matrix.h"
-#include "Outline.h"
-#include "Rect.h"
-#include "utils/Macros.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Temporary structure holding information for a single outline clip.
- *
- * These structures are treated as immutable once created, and only exist for a single frame, which
- * is why they may only be allocated with a LinearAllocator.
- */
-class RoundRectClipState {
-public:
-    static void* operator new(size_t size) = delete;
-    static void* operator new(size_t size, LinearAllocator& allocator) {
-        return allocator.alloc<RoundRectClipState>(size);
-    }
-
-    bool areaRequiresRoundRectClip(const Rect& rect) const {
-        return rect.intersects(dangerRects[0]) || rect.intersects(dangerRects[1]) ||
-               rect.intersects(dangerRects[2]) || rect.intersects(dangerRects[3]);
-    }
-
-    bool highPriority;
-    Matrix4 matrix;
-    Rect dangerRects[4];
-    Rect innerRect;
-    float radius;
-};
-
-/**
- * A snapshot holds information about the current state of the rendering
- * surface. A snapshot is usually created whenever the user calls save()
- * and discarded when the user calls restore(). Once a snapshot is created,
- * it can hold information for deferred rendering.
- *
- * Each snapshot has a link to a previous snapshot, indicating the previous
- * state of the renderer.
- */
-class Snapshot {
-public:
-    Snapshot();
-    Snapshot(Snapshot* s, int saveFlags);
-
-    /**
-     * Various flags set on ::flags.
-     */
-    enum Flags {
-        /**
-         * Indicates that the clip region was modified. When this
-         * snapshot is restored so must the clip.
-         */
-        kFlagClipSet = 0x1,
-        /**
-         * Indicates that this snapshot was created when saving
-         * a new layer.
-         */
-        kFlagIsLayer = 0x2,
-        /**
-         * Indicates that this snapshot is a special type of layer
-         * backed by an FBO. This flag only makes sense when the
-         * flag kFlagIsLayer is also set.
-         *
-         * Viewport has been modified to fit the new Fbo, and must be
-         * restored when this snapshot is restored.
-         */
-        kFlagIsFboLayer = 0x4,
-    };
-
-    /**
-     * Modifies the current clip with the new clip rectangle and
-     * the specified operation. The specified rectangle is transformed
-     * by this snapshot's trasnformation.
-     */
-    void clip(const Rect& localClip, SkClipOp op);
-
-    /**
-     * Modifies the current clip with the new clip rectangle and
-     * the specified operation. The specified rectangle is considered
-     * already transformed.
-     */
-    void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect);
-
-    /**
-     * Modifies the current clip with the specified path and operation.
-     */
-    void clipPath(const SkPath& path, SkClipOp op);
-
-    /**
-     * Sets the current clip.
-     */
-    void setClip(float left, float top, float right, float bottom);
-
-    /**
-     * Returns the current clip in local coordinates. The clip rect is
-     * transformed by the inverse transform matrix.
-     */
-    ANDROID_API const Rect& getLocalClip();
-
-    /**
-     * Returns the current clip in render target coordinates.
-     */
-    const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); }
-
-    /*
-     * Accessor functions so that the clip area can stay private
-     */
-    bool clipIsEmpty() const { return mClipArea->isEmpty(); }
-    const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
-    bool clipIsSimple() const { return mClipArea->isSimple(); }
-    const ClipArea& getClipArea() const { return *mClipArea; }
-    ClipArea& mutateClipArea() { return *mClipArea; }
-
-    WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
-            LinearAllocator& allocator, const ClipBase* recordedClip,
-            const Matrix4& recordedClipTransform);
-    void applyClip(const ClipBase* clip, const Matrix4& transform);
-
-    /**
-     * Resets the clip to the specified rect.
-     */
-    void resetClip(float left, float top, float right, float bottom);
-
-    void initializeViewport(int width, int height) {
-        mViewportData.initialize(width, height);
-        mClipAreaRoot.setViewportDimensions(width, height);
-    }
-
-    int getViewportWidth() const { return mViewportData.mWidth; }
-    int getViewportHeight() const { return mViewportData.mHeight; }
-    const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
-
-    const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; }
-    void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; }
-
-    /**
-     * Sets (and replaces) the current clipping outline
-     *
-     * If the current round rect clip is high priority, the incoming clip is ignored.
-     */
-    void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds, float radius,
-                              bool highPriority);
-
-    /**
-     * Sets (and replaces) the current projection mask
-     */
-    void setProjectionPathMask(const SkPath* path);
-
-    /**
-     * Indicates whether the current transform has perspective components.
-     */
-    bool hasPerspectiveTransform() const;
-
-    /**
-     * Dirty flags.
-     */
-    int flags;
-
-    /**
-     * Previous snapshot.
-     */
-    Snapshot* previous;
-
-    /**
-     * A pointer to the currently active layer.
-     *
-     * This snapshot does not own the layer, this pointer must not be freed.
-     */
-    Layer* layer;
-
-    /**
-     * Target FBO used for rendering. Set to 0 when rendering directly
-     * into the framebuffer.
-     */
-    GLuint fbo;
-
-    /**
-     * Local transformation. Holds the current translation, scale and
-     * rotation values.
-     *
-     * This is a reference to a matrix owned by this snapshot or another
-     *  snapshot. This pointer must not be freed. See ::mTransformRoot.
-     */
-    mat4* transform;
-
-    /**
-     * Current alpha value. This value is 1 by default, but may be set by a DisplayList which
-     * has translucent rendering in a non-overlapping View. This value will be used by
-     * the renderer to set the alpha in the current color being used for ensuing drawing
-     * operations. The value is inherited by child snapshots because the same value should
-     * be applied to descendants of the current DisplayList (for example, a TextView contains
-     * the base alpha value which should be applied to the child DisplayLists used for drawing
-     * the actual text).
-     */
-    float alpha;
-
-    /**
-     * Current clipping round rect.
-     *
-     * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips,
-     * never modified.
-     */
-    const RoundRectClipState* roundRectClipState;
-
-    /**
-     * Current projection masking path - used exclusively to mask projected, tessellated circles.
-     */
-    const SkPath* projectionPathMask;
-
-    void dump() const;
-
-private:
-    struct ViewportData {
-        ViewportData() : mWidth(0), mHeight(0) {}
-        void initialize(int width, int height) {
-            mWidth = width;
-            mHeight = height;
-            mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
-        }
-
-        /*
-         * Width and height of current viewport.
-         *
-         * The viewport is always defined to be (0, 0, width, height).
-         */
-        int mWidth;
-        int mHeight;
-        /**
-         * Contains the current orthographic, projection matrix.
-         */
-        mat4 mOrthoMatrix;
-    };
-
-    mat4 mTransformRoot;
-
-    ClipArea mClipAreaRoot;
-    ClipArea* mClipArea;
-    Rect mLocalClip;
-
-    ViewportData mViewportData;
-    Vector3 mRelativeLightCenter;
-
-};  // class Snapshot
-
-};  // namespace uirenderer
-};  // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index a6aae55..f091277 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -19,7 +19,6 @@
 
 #include "Vector.h"
 
-#include "FloatColor.h"
 #include "utils/Macros.h"
 
 namespace android {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b04194f..753557c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -224,6 +224,7 @@
             break;
         case PixelStorageType::Heap:
             free(mPixelStorage.heap.address);
+            mallopt(M_PURGE, 0);
             break;
         case PixelStorageType::Hardware:
             auto buffer = mPixelStorage.hardware.buffer;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 0022c93..c6e4c15 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -141,7 +141,7 @@
     // select only flags that might affect text layout
     flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
               SkPaint::kSubpixelText_Flag | SkPaint::kEmbeddedBitmapText_Flag |
-              SkPaint::kAutoHinting_Flag | SkPaint::kVerticalText_Flag);
+              SkPaint::kAutoHinting_Flag);
     flags |= (hinting << 16);
     return flags;
 }
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 3ca0f81..13d2dae 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -71,15 +71,12 @@
         paint.setAlpha(layer->getAlpha());
         paint.setBlendMode(layer->getMode());
         paint.setColorFilter(layer->getColorSpaceWithFilter());
-        if (layer->getForceFilter()) {
-            paint.setFilterQuality(kLow_SkFilterQuality);
-        }
-
         const bool nonIdentityMatrix = !matrix.isIdentity();
         if (nonIdentityMatrix) {
             canvas->save();
             canvas->concat(matrix);
         }
+        const SkMatrix& totalMatrix = canvas->getTotalMatrix();
         if (dstRect || srcRect) {
             SkMatrix matrixInv;
             if (!matrix.invert(&matrixInv)) {
@@ -99,9 +96,28 @@
                 skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight);
             }
             matrixInv.mapRect(&skiaDestRect);
+            // If (matrix is identity or an integer translation) and (src/dst buffers size match),
+            // then use nearest neighbor, otherwise use bilerp sampling.
+            // Integer translation is defined as when src rect and dst rect align fractionally.
+            // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
+            // only for SrcOver blending and without color filter (readback uses Src blending).
+            bool isIntegerTranslate = totalMatrix.isTranslate()
+                    && SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
+                    == SkScalarFraction(skiaSrcRect.fLeft)
+                    && SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
+                    == SkScalarFraction(skiaSrcRect.fTop);
+            if (layer->getForceFilter() || !isIntegerTranslate) {
+                paint.setFilterQuality(kLow_SkFilterQuality);
+            }
             canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
                                   SkCanvas::kFast_SrcRectConstraint);
         } else {
+            bool isIntegerTranslate = totalMatrix.isTranslate()
+                    && SkScalarIsInt(totalMatrix[SkMatrix::kMTransX])
+                    && SkScalarIsInt(totalMatrix[SkMatrix::kMTransY]);
+            if (layer->getForceFilter() || !isIntegerTranslate) {
+                paint.setFilterQuality(kLow_SkFilterQuality);
+            }
             canvas->drawImage(layerImage.get(), 0, 0, &paint);
         }
         // restore the original matrix
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index a00b8db..c5db861d 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -21,7 +21,6 @@
 #include <Properties.h>
 #include <Rect.h>
 #include <RenderNode.h>
-#include <Snapshot.h>
 #include <hwui/Bitmap.h>
 #include <pipeline/skia/SkiaRecordingCanvas.h>
 #include <private/hwui/DrawGlInfo.h>
@@ -141,14 +140,6 @@
         return true;
     }
 
-    static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
-        std::unique_ptr<Snapshot> snapshot(new Snapshot());
-        // store clip first, so it isn't transformed
-        snapshot->setClip(clip.left, clip.top, clip.right, clip.bottom);
-        *(snapshot->transform) = transform;
-        return snapshot;
-    }
-
     static sk_sp<Bitmap> createBitmap(int width, int height,
                                       SkColorType colorType = kN32_SkColorType) {
         SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index e3c97ce..524dfb0 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -288,7 +288,7 @@
 
             case '?':
                 fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
-            // fall-through
+                [[fallthrough]];
             default:
                 error = true;
                 break;
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 9388c20..70423a7 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -16,7 +16,6 @@
 
 #include <benchmark/benchmark.h>
 
-#include "CanvasState.h"
 #include "DisplayList.h"
 #include "hwui/Canvas.h"
 #include "pipeline/skia/SkiaDisplayList.h"
@@ -116,52 +115,6 @@
 }
 BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView);
 
-class NullClient : public CanvasStateClient {
-    void onViewportInitialized() override {}
-    void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-    GLuint getTargetFbo() const override { return 0; }
-};
-
-void BM_CanvasState_saverestore(benchmark::State& benchState) {
-    NullClient client;
-    CanvasState state(client);
-    state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
-    while (benchState.KeepRunning()) {
-        state.save(SaveFlags::MatrixClip);
-        state.save(SaveFlags::MatrixClip);
-        benchmark::DoNotOptimize(&state);
-        state.restore();
-        state.restore();
-    }
-}
-BENCHMARK(BM_CanvasState_saverestore);
-
-void BM_CanvasState_init(benchmark::State& benchState) {
-    NullClient client;
-    CanvasState state(client);
-    state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
-    while (benchState.KeepRunning()) {
-        state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-        benchmark::DoNotOptimize(&state);
-    }
-}
-BENCHMARK(BM_CanvasState_init);
-
-void BM_CanvasState_translate(benchmark::State& benchState) {
-    NullClient client;
-    CanvasState state(client);
-    state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
-
-    while (benchState.KeepRunning()) {
-        state.translate(5, 5, 0);
-        benchmark::DoNotOptimize(&state);
-        state.translate(-5, -5, 0);
-    }
-}
-BENCHMARK(BM_CanvasState_translate);
-
 void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) {
     sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, [](auto& props, auto& canvas) {
         canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver);
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
deleted file mode 100644
index 4c03811..0000000
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CanvasState.h"
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "hwui/Canvas.h"
-#include "utils/LinearAllocator.h"
-
-#include <SkClipOp.h>
-#include <SkPath.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace uirenderer {
-
-class NullClient : public CanvasStateClient {
-    void onViewportInitialized() override {}
-    void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-    GLuint getTargetFbo() const override { return 0; }
-};
-
-static NullClient sNullClient;
-
-static bool approxEqual(const Matrix4& a, const Matrix4& b) {
-    for (int i = 0; i < 16; i++) {
-        if (!MathUtils::areEqual(a[i], b[i])) {
-            return false;
-        }
-    }
-    return true;
-}
-
-TEST(CanvasState, gettersAndSetters) {
-    CanvasState state(sNullClient);
-    state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
-    ASSERT_EQ(state.getWidth(), 200);
-    ASSERT_EQ(state.getHeight(), 200);
-
-    Matrix4 simpleTranslate;
-    simpleTranslate.loadTranslate(10, 20, 0);
-    state.setMatrix(simpleTranslate);
-
-    ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200));
-    ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180));
-    EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
-    EXPECT_TRUE(state.clipIsSimple());
-}
-
-TEST(CanvasState, simpleClipping) {
-    CanvasState state(sNullClient);
-    state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
-    state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
-    ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
-
-    state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
-    ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
-
-    state.clipRect(50, 50, 150, 150, SkClipOp::kReplace_deprecated);
-    ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
-}
-
-TEST(CanvasState, complexClipping) {
-    CanvasState state(sNullClient);
-    state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
-    state.save(SaveFlags::MatrixClip);
-    {
-        // rotated clip causes complex clip
-        state.rotate(10);
-        EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
-        EXPECT_FALSE(state.clipIsSimple());
-    }
-    state.restore();
-
-    state.save(SaveFlags::MatrixClip);
-    {
-        // subtracted clip causes complex clip
-        EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(50, 50, 150, 150, SkClipOp::kDifference);
-        EXPECT_FALSE(state.clipIsSimple());
-    }
-    state.restore();
-
-    state.save(SaveFlags::MatrixClip);
-    {
-        // complex path causes complex clip
-        SkPath path;
-        path.addOval(SkRect::MakeWH(200, 200));
-        EXPECT_TRUE(state.clipIsSimple());
-        state.clipPath(&path, SkClipOp::kDifference);
-        EXPECT_FALSE(state.clipIsSimple());
-    }
-    state.restore();
-}
-
-TEST(CanvasState, saveAndRestore) {
-    CanvasState state(sNullClient);
-    state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
-    state.save(SaveFlags::Clip);
-    {
-        state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
-        ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
-    }
-    state.restore();
-    ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200));  // verify restore
-
-    Matrix4 simpleTranslate;
-    simpleTranslate.loadTranslate(10, 10, 0);
-    state.save(SaveFlags::Matrix);
-    {
-        state.translate(10, 10, 0);
-        EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
-    }
-    state.restore();
-    EXPECT_FALSE(approxEqual(*state.currentTransform(), simpleTranslate));
-}
-
-TEST(CanvasState, saveAndRestoreButNotTooMuch) {
-    CanvasState state(sNullClient);
-    state.initializeSaveStack(200, 200, 0, 0, 200, 200, Vector3());
-
-    state.save(SaveFlags::Matrix);  // NOTE: clip not saved
-    {
-        state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
-        ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
-    }
-    state.restore();
-    ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));  // verify not restored
-
-    Matrix4 simpleTranslate;
-    simpleTranslate.loadTranslate(10, 10, 0);
-    state.save(SaveFlags::Clip);  // NOTE: matrix not saved
-    {
-        state.translate(10, 10, 0);
-        EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
-    }
-    state.restore();
-    EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));  // verify not restored
-}
-}
-}
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
deleted file mode 100644
index 450bb67..0000000
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <SkPath.h>
-#include <SkRegion.h>
-#include <gtest/gtest.h>
-
-#include "ClipArea.h"
-
-#include "Matrix.h"
-#include "Rect.h"
-#include "utils/LinearAllocator.h"
-
-namespace android {
-namespace uirenderer {
-
-static Rect kViewportBounds(2048, 2048);
-
-static ClipArea createClipArea() {
-    ClipArea area;
-    area.setViewportDimensions(kViewportBounds.getWidth(), kViewportBounds.getHeight());
-    return area;
-}
-
-TEST(TransformedRectangle, basics) {
-    Rect r(0, 0, 100, 100);
-    Matrix4 minus90;
-    minus90.loadRotate(-90);
-    minus90.mapRect(r);
-    Rect r2(20, 40, 120, 60);
-
-    Matrix4 m90;
-    m90.loadRotate(90);
-    TransformedRectangle tr(r, m90);
-    EXPECT_TRUE(tr.canSimplyIntersectWith(tr));
-
-    Matrix4 m0;
-    TransformedRectangle tr0(r2, m0);
-    EXPECT_FALSE(tr.canSimplyIntersectWith(tr0));
-
-    Matrix4 m45;
-    m45.loadRotate(45);
-    TransformedRectangle tr2(r, m45);
-    EXPECT_FALSE(tr2.canSimplyIntersectWith(tr));
-}
-
-TEST(RectangleList, basics) {
-    RectangleList list;
-    EXPECT_TRUE(list.isEmpty());
-
-    Rect r(0, 0, 100, 100);
-    Matrix4 m45;
-    m45.loadRotate(45);
-    list.set(r, m45);
-    EXPECT_FALSE(list.isEmpty());
-
-    Rect r2(20, 20, 200, 200);
-    list.intersectWith(r2, m45);
-    EXPECT_FALSE(list.isEmpty());
-    EXPECT_EQ(1, list.getTransformedRectanglesCount());
-
-    Rect r3(20, 20, 200, 200);
-    Matrix4 m30;
-    m30.loadRotate(30);
-    list.intersectWith(r2, m30);
-    EXPECT_FALSE(list.isEmpty());
-    EXPECT_EQ(2, list.getTransformedRectanglesCount());
-
-    SkRegion clip;
-    clip.setRect(0, 0, 2000, 2000);
-    SkRegion rgn(list.convertToRegion(clip));
-    EXPECT_FALSE(rgn.isEmpty());
-}
-
-TEST(ClipArea, basics) {
-    ClipArea area(createClipArea());
-    EXPECT_FALSE(area.isEmpty());
-}
-
-TEST(ClipArea, paths) {
-    ClipArea area(createClipArea());
-    SkPath path;
-    SkScalar r = 100;
-    path.addCircle(r, r, r);
-    area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op);
-    EXPECT_FALSE(area.isEmpty());
-    EXPECT_FALSE(area.isSimple());
-    EXPECT_FALSE(area.isRectangleList());
-
-    Rect clipRect(area.getClipRect());
-    Rect expected(0, 0, r * 2, r * 2);
-    EXPECT_EQ(expected, clipRect);
-    SkRegion clipRegion(area.getClipRegion());
-    auto skRect(clipRegion.getBounds());
-    Rect regionBounds;
-    regionBounds.set(skRect);
-    EXPECT_EQ(expected, regionBounds);
-}
-
-TEST(ClipArea, replaceNegative) {
-    ClipArea area(createClipArea());
-    area.setClip(0, 0, 100, 100);
-
-    Rect expected(-50, -50, 50, 50);
-    area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op);
-    EXPECT_EQ(expected, area.getClipRect());
-}
-
-TEST(ClipArea, serializeClip) {
-    ClipArea area(createClipArea());
-    LinearAllocator allocator;
-
-    // unset clip
-    EXPECT_EQ(nullptr, area.serializeClip(allocator));
-
-    // rect clip
-    area.setClip(0, 0, 200, 200);
-    {
-        auto serializedClip = area.serializeClip(allocator);
-        ASSERT_NE(nullptr, serializedClip);
-        ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode);
-        ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
-        EXPECT_EQ(Rect(200, 200), serializedClip->rect);
-        EXPECT_EQ(serializedClip, area.serializeClip(allocator))
-                << "Requery of clip on unmodified ClipArea must return same pointer.";
-    }
-
-    // rect list
-    Matrix4 rotate;
-    rotate.loadRotate(5.0f);
-    area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op);
-    {
-        auto serializedClip = area.serializeClip(allocator);
-        ASSERT_NE(nullptr, serializedClip);
-        ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
-        ASSERT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
-        auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
-        EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
-        EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect);
-        EXPECT_EQ(serializedClip, area.serializeClip(allocator))
-                << "Requery of clip on unmodified ClipArea must return same pointer.";
-    }
-
-    // region
-    SkPath circlePath;
-    circlePath.addCircle(100, 100, 100);
-    area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
-    {
-        auto serializedClip = area.serializeClip(allocator);
-        ASSERT_NE(nullptr, serializedClip);
-        ASSERT_EQ(ClipMode::Region, serializedClip->mode);
-        ASSERT_TRUE(serializedClip->intersectWithRoot) << "Replace op, so expect intersectWithRoot";
-        auto clipRegion = reinterpret_cast<const ClipRegion*>(serializedClip);
-        EXPECT_EQ(SkIRect::MakeWH(200, 200), clipRegion->region.getBounds())
-                << "Clip region should be 200x200";
-        EXPECT_EQ(Rect(200, 200), clipRegion->rect);
-        EXPECT_EQ(serializedClip, area.serializeClip(allocator))
-                << "Requery of clip on unmodified ClipArea must return same pointer.";
-    }
-}
-
-TEST(ClipArea, serializeClip_pathIntersectWithRoot) {
-    ClipArea area(createClipArea());
-    LinearAllocator allocator;
-    SkPath circlePath;
-    circlePath.addCircle(100, 100, 100);
-    area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kIntersect_Op);
-
-    auto serializedClip = area.serializeClip(allocator);
-    ASSERT_NE(nullptr, serializedClip);
-    EXPECT_FALSE(serializedClip->intersectWithRoot) << "No replace, so no intersectWithRoot";
-}
-
-TEST(ClipArea, serializeIntersectedClip) {
-    ClipArea area(createClipArea());
-    LinearAllocator allocator;
-
-    // simple state;
-    EXPECT_EQ(nullptr, area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
-    area.setClip(0, 0, 200, 200);
-    {
-        auto origRectClip = area.serializeClip(allocator);
-        ASSERT_NE(nullptr, origRectClip);
-        EXPECT_EQ(origRectClip,
-                  area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity()));
-    }
-
-    // rect
-    {
-        ClipRect recordedClip(Rect(100, 100));
-        Matrix4 translateScale;
-        translateScale.loadTranslate(100, 100, 0);
-        translateScale.scale(2, 3, 1);
-        auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
-        ASSERT_NE(nullptr, resolvedClip);
-        ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode);
-        EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect);
-
-        EXPECT_EQ(resolvedClip,
-                  area.serializeIntersectedClip(allocator, &recordedClip, translateScale))
-                << "Must return previous serialization, since input is same";
-
-        ClipRect recordedClip2(Rect(100, 100));
-        EXPECT_NE(resolvedClip,
-                  area.serializeIntersectedClip(allocator, &recordedClip2, translateScale))
-                << "Shouldn't return previous serialization, since matrix location is different";
-    }
-
-    // rect list
-    Matrix4 rotate;
-    rotate.loadRotate(2.0f);
-    area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
-    {
-        ClipRect recordedClip(Rect(100, 100));
-        auto resolvedClip =
-                area.serializeIntersectedClip(allocator, &recordedClip, Matrix4::identity());
-        ASSERT_NE(nullptr, resolvedClip);
-        ASSERT_EQ(ClipMode::RectangleList, resolvedClip->mode);
-        auto clipRectList = reinterpret_cast<const ClipRectList*>(resolvedClip);
-        EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
-    }
-
-    // region
-    SkPath circlePath;
-    circlePath.addCircle(100, 100, 100);
-    area.clipPathWithTransform(circlePath, &Matrix4::identity(), SkRegion::kReplace_Op);
-    {
-        SkPath ovalPath;
-        ovalPath.addOval(SkRect::MakeLTRB(50, 0, 150, 200));
-
-        ClipRegion recordedClip;
-        recordedClip.region.setPath(ovalPath, SkRegion(SkIRect::MakeWH(200, 200)));
-        recordedClip.rect = Rect(200, 200);
-
-        Matrix4 translate10x20;
-        translate10x20.loadTranslate(10, 20, 0);
-        auto resolvedClip = area.serializeIntersectedClip(
-                allocator, &recordedClip,
-                translate10x20);  // Note: only translate for now, others not handled correctly
-        ASSERT_NE(nullptr, resolvedClip);
-        ASSERT_EQ(ClipMode::Region, resolvedClip->mode);
-        auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
-        EXPECT_EQ(SkIRect::MakeLTRB(60, 20, 160, 200), clipRegion->region.getBounds());
-    }
-}
-
-TEST(ClipArea, serializeIntersectedClip_snap) {
-    ClipArea area(createClipArea());
-    area.setClip(100.2, 100.4, 500.6, 500.8);
-    LinearAllocator allocator;
-
-    {
-        // no recorded clip case
-        auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity());
-        EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect);
-    }
-    {
-        // recorded clip case
-        ClipRect recordedClip(Rect(100.12, 100.74));
-        Matrix4 translateScale;
-        translateScale.loadTranslate(100, 100, 0);
-        translateScale.scale(2, 3,
-                             1);  // recorded clip will have non-int coords, even after transform
-        auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
-        ASSERT_NE(nullptr, resolvedClip);
-        EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode);
-        EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect);
-    }
-}
-
-TEST(ClipArea, serializeIntersectedClip_scale) {
-    ClipArea area(createClipArea());
-    area.setClip(0, 0, 400, 400);
-    LinearAllocator allocator;
-
-    SkPath circlePath;
-    circlePath.addCircle(50, 50, 50);
-
-    ClipRegion recordedClip;
-    recordedClip.region.setPath(circlePath, SkRegion(SkIRect::MakeWH(100, 100)));
-    recordedClip.rect = Rect(100, 100);
-
-    Matrix4 translateScale;
-    translateScale.loadTranslate(100, 100, 0);
-    translateScale.scale(2, 2, 1);
-    auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
-
-    ASSERT_NE(nullptr, resolvedClip);
-    EXPECT_EQ(ClipMode::Region, resolvedClip->mode);
-    EXPECT_EQ(Rect(100, 100, 300, 300), resolvedClip->rect);
-    auto clipRegion = reinterpret_cast<const ClipRegion*>(resolvedClip);
-    EXPECT_EQ(SkIRect::MakeLTRB(100, 100, 300, 300), clipRegion->region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_identity) {
-    SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
-    ClipArea::applyTransformToRegion(Matrix4::identity(), &region);
-    EXPECT_TRUE(region.isRect());
-    EXPECT_EQ(SkIRect::MakeLTRB(1, 2, 3, 4), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_translate) {
-    SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
-    Matrix4 transform;
-    transform.loadTranslate(10, 20, 0);
-    ClipArea::applyTransformToRegion(transform, &region);
-    EXPECT_TRUE(region.isRect());
-    EXPECT_EQ(SkIRect::MakeLTRB(11, 22, 13, 24), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_scale) {
-    SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
-    Matrix4 transform;
-    transform.loadScale(2, 3, 1);
-    ClipArea::applyTransformToRegion(transform, &region);
-    EXPECT_TRUE(region.isRect());
-    EXPECT_EQ(SkIRect::MakeLTRB(2, 6, 6, 12), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_translateScale) {
-    SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
-    Matrix4 transform;
-    transform.translate(10, 20);
-    transform.scale(2, 3, 1);
-    ClipArea::applyTransformToRegion(transform, &region);
-    EXPECT_TRUE(region.isRect());
-    EXPECT_EQ(SkIRect::MakeLTRB(12, 26, 16, 32), region.getBounds());
-}
-
-TEST(ClipArea, applyTransformToRegion_rotate90) {
-    SkRegion region(SkIRect::MakeLTRB(1, 2, 3, 4));
-    Matrix4 transform;
-    transform.loadRotate(90);
-    ClipArea::applyTransformToRegion(transform, &region);
-    EXPECT_TRUE(region.isRect());
-    EXPECT_EQ(SkIRect::MakeLTRB(-4, 1, -2, 3), region.getBounds());
-}
-
-}  // namespace uirenderer
-}  // namespace android
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2926ef3..2c73940 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -32,6 +32,7 @@
 #include "pipeline/skia/SkiaRecordingCanvas.h"
 #include "renderthread/CanvasContext.h"
 #include "tests/common/TestUtils.h"
+#include "utils/Color.h"
 
 using namespace android;
 using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp
deleted file mode 100644
index 9d673c8..0000000
--- a/libs/hwui/tests/unit/SnapshotTests.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <Snapshot.h>
-
-#include <tests/common/TestUtils.h>
-
-using namespace android::uirenderer;
-
-TEST(Snapshot, serializeIntersectedClip) {
-    auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
-    auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
-    auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
-    root->previous = actualRoot.get();
-    child->previous = root.get();
-
-    LinearAllocator allocator;
-    ClipRect rect(Rect(0, 0, 75, 75));
-    {
-        auto intersectWithChild =
-                child->serializeIntersectedClip(allocator, &rect, Matrix4::identity());
-        ASSERT_NE(nullptr, intersectWithChild);
-        EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child";
-    }
-
-    rect.intersectWithRoot = true;
-    {
-        auto intersectWithRoot =
-                child->serializeIntersectedClip(allocator, &rect, Matrix4::identity());
-        ASSERT_NE(nullptr, intersectWithRoot);
-        EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root";
-    }
-}
-
-TEST(Snapshot, applyClip) {
-    auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
-    auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
-    root->previous = actualRoot.get();
-
-    ClipRect rect(Rect(0, 0, 75, 75));
-    {
-        auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
-        child->previous = root.get();
-        child->applyClip(&rect, Matrix4::identity());
-
-        EXPECT_TRUE(child->getClipArea().isSimple());
-        EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip());
-    }
-
-    {
-        rect.intersectWithRoot = true;
-        auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
-        child->previous = root.get();
-        child->applyClip(&rect, Matrix4::identity());
-
-        EXPECT_TRUE(child->getClipArea().isSimple());
-        EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip());
-    }
-}
diff --git a/media/OWNERS b/media/OWNERS
index 1ae2a7b..0abf9ae 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -9,4 +9,4 @@
 wjia@google.com
 jaewan@google.com
 chz@google.com
-
+dwkang@google.com
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 38204a5..a02b6af 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1068,6 +1068,7 @@
         ENCODING_E_AC3_JOC,
         ENCODING_DTS,
         ENCODING_DTS_HD,
+        ENCODING_MP3,
         ENCODING_IEC61937,
         ENCODING_AAC_HE_V1,
         ENCODING_AAC_HE_V2,
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c074cce..274da11 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1379,14 +1379,20 @@
     //====================================================================
     // Offload query
     /**
-     * @hide
      * Returns whether offloaded playback of an audio format is supported on the device.
-     * Offloaded playback is where the decoding of an audio stream is not competing with other
-     * software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
+     * <p>Offloaded playback is the feature where the decoding and playback of an audio stream
+     * is not competing with other software resources. In general, it is supported by dedicated
+     * hardware, such as audio DSPs.
+     * <p>Note that this query only provides information about the support of an audio format,
+     * it does not indicate whether the resources necessary for the offloaded playback are
+     * available at that instant.
      * @param format the audio format (codec, sample rate, channels) being checked.
      * @return true if the given audio format can be offloaded.
      */
-    public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+    public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+        if (format == null) {
+            throw new IllegalArgumentException("Illegal null AudioFormat");
+        }
         return AudioSystem.isOffloadSupported(format);
     }
 
@@ -3942,18 +3948,31 @@
     }
 
      /**
-     * Indicate Hearing Aid connection state change.
+     * Indicate Hearing Aid connection state change and eventually suppress
+     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
      * @param device Bluetooth device connected/disconnected
      * @param state new connection state (BluetoothProfile.STATE_xxx)
+     * @param musicDevice Default get system volume for the connecting device.
+     * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or
+     * {@link android.bluetooth.BluetoothProfile.HEARING_AID})
+     * @param suppressNoisyIntent if true the
+     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     * @return a delay in ms that the caller should wait before broadcasting
+     * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent.
      * {@hide}
      */
-    public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) {
+    public int setBluetoothHearingAidDeviceConnectionState(
+                BluetoothDevice device, int state, boolean suppressNoisyIntent,
+                int musicDevice) {
         final IAudioService service = getService();
+        int delay = 0;
         try {
-            service.setHearingAidDeviceConnectionState(device, state);
+            delay = service.setBluetoothHearingAidDeviceConnectionState(device,
+                state, suppressNoisyIntent, musicDevice);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+        return delay;
     }
 
      /**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e8fe9fe..67cc456 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.audiopolicy.AudioMix;
+import android.os.Build;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -864,9 +865,9 @@
     public static native int setMasterMono(boolean mono);
 
     // helpers for android.media.AudioManager.getProperty(), see description there for meaning
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552)
     public static native int getPrimaryOutputSamplingRate();
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552)
     public static native int getPrimaryOutputFrameCount();
     @UnsupportedAppUsage
     public static native int getOutputLatency(int stream);
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 62e58ca..3ec595d 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,7 +23,7 @@
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.NioUtils;
-import java.util.Collection;
+import java.util.LinkedList;
 import java.util.concurrent.Executor;
 
 import android.annotation.CallbackExecutor;
@@ -31,16 +31,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
-import android.app.ActivityThread;
-import android.content.Context;
+import android.os.Build;
+import android.os.Binder;
 import android.os.Handler;
-import android.os.IBinder;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -191,9 +188,8 @@
     private static final int NATIVE_EVENT_NEW_POS = 4;
     /**
      * Callback for more data
-     * TODO only for offload
      */
-    private static final int NATIVE_EVENT_MORE_DATA = 0;
+    private static final int NATIVE_EVENT_CAN_WRITE_MORE_DATA = 9;
     /**
      * IAudioTrack tear down for offloaded tracks
      * TODO: when received, java AudioTrack must be released
@@ -202,7 +198,6 @@
     /**
      * Event id denotes when all the buffers queued in AF and HW are played
      * back (after stop is called) for an offloaded track.
-     * TODO: not just for offload
      */
     private static final int NATIVE_EVENT_STREAM_END = 7;
 
@@ -392,6 +387,10 @@
      * Offset of the first sample of the audio in byte from start of HW_AV_SYNC track AV header.
      */
     private int mOffset = 0;
+    /**
+     * Indicates whether the track is intended to play in offload mode.
+     */
+    private boolean mOffloaded = false;
 
     //--------------------------------
     // Used exclusively by native code
@@ -614,6 +613,7 @@
             encoding = format.getEncoding();
         }
         audioParamCheck(rate, channelMask, channelIndexMask, encoding, mode);
+        mOffloaded = offload;
         mStreamType = AudioSystem.STREAM_DEFAULT;
 
         audioBuffSizeCheck(bufferSizeInBytes);
@@ -901,7 +901,6 @@
         }
 
         /**
-         * @hide
          * Sets whether this track will play through the offloaded audio path.
          * When set to true, at build time, the audio format will be checked against
          * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
@@ -960,8 +959,11 @@
                         .build();
             }
 
-            //TODO tie offload to PERFORMANCE_MODE_POWER_SAVING?    
             if (mOffload) {
+                if (mPerformanceMode == PERFORMANCE_MODE_LOW_LATENCY) {
+                    throw new UnsupportedOperationException(
+                            "Offload and low latency modes are incompatible");
+                }
                 if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
                     throw new UnsupportedOperationException(
                             "Cannot create AudioTrack, offload requires USAGE_MEDIA");
@@ -1223,6 +1225,9 @@
      * Releases the native AudioTrack resources.
      */
     public void release() {
+        synchronized (mStreamEventCbLock){
+            endStreamEventHandling();
+        }
         // even though native_release() stops the native AudioTrack, we need to stop
         // AudioTrack subclasses too.
         try {
@@ -1506,7 +1511,7 @@
      * a better solution.
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552)
     public int getLatency() {
         return native_get_latency();
     }
@@ -2282,7 +2287,8 @@
             return ERROR_BAD_VALUE;
         }
 
-        int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
+
+        final int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
                 writeMode == WRITE_BLOCKING);
 
         if ((mDataLoadMode == MODE_STATIC)
@@ -2391,7 +2397,7 @@
             return ERROR_BAD_VALUE;
         }
 
-        int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat,
+        final int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat,
                 writeMode == WRITE_BLOCKING);
 
         if ((mDataLoadMode == MODE_STATIC)
@@ -2479,7 +2485,7 @@
             return ERROR_BAD_VALUE;
         }
 
-        int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
+        final int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
                 writeMode == WRITE_BLOCKING);
 
         if ((mDataLoadMode == MODE_STATIC)
@@ -2986,68 +2992,205 @@
     }
 
     /**
-     * @hide
-     * Abstract class to receive event notification about the stream playback.
-     * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
+     * Abstract class to receive event notifications about the stream playback in offloaded mode.
+     * See {@link AudioTrack#registerStreamEventCallback(Executor, StreamEventCallback)} to register
      * the callback on the given {@link AudioTrack} instance.
      */
     public abstract static class StreamEventCallback {
-        /** @hide */ // add hidden empty constructor so it doesn't show in SDK
-        public StreamEventCallback() { }
         /**
          * Called when an offloaded track is no longer valid and has been discarded by the system.
          * An example of this happening is when an offloaded track has been paused too long, and
          * gets invalidated by the system to prevent any other offload.
-         * @param track the {@link AudioTrack} on which the event happened
+         * @param track the {@link AudioTrack} on which the event happened.
          */
         public void onTearDown(AudioTrack track) { }
         /**
          * Called when all the buffers of an offloaded track that were queued in the audio system
          * (e.g. the combination of the Android audio framework and the device's audio hardware)
          * have been played after {@link AudioTrack#stop()} has been called.
-         * @param track the {@link AudioTrack} on which the event happened
+         * @param track the {@link AudioTrack} on which the event happened.
          */
-        public void onStreamPresentationEnd(AudioTrack track) { }
+        public void onPresentationEnded(AudioTrack track) { }
         /**
          * Called when more audio data can be written without blocking on an offloaded track.
-         * @param track the {@link AudioTrack} on which the event happened
+         * @param track the {@link AudioTrack} on which the event happened.
+         * @param sizeInFrames the number of frames available to write without blocking.
+         *   Note that the frame size of a compressed stream is 1 byte.
          */
-        public void onStreamDataRequest(AudioTrack track) { }
+        public void onDataRequest(AudioTrack track, int sizeInFrames) { }
     }
 
-    private Executor mStreamEventExec;
-    private StreamEventCallback mStreamEventCb;
-    private final Object mStreamEventCbLock = new Object();
-
     /**
-     * @hide
-     * Sets the callback for the notification of stream events.
-     * @param executor {@link Executor} to handle the callbacks
-     * @param eventCallback the callback to receive the stream event notifications
+     * Registers a callback for the notification of stream events.
+     * This callback can only be registered for instances operating in offloaded mode
+     * (see {@link AudioTrack.Builder#setOffloadedPlayback(boolean)} and
+     * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} for more details).
+     * @param executor {@link Executor} to handle the callbacks.
+     * @param eventCallback the callback to receive the stream event notifications.
      */
-    public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull StreamEventCallback eventCallback) {
         if (eventCallback == null) {
             throw new IllegalArgumentException("Illegal null StreamEventCallback");
         }
+        if (!mOffloaded) {
+            throw new IllegalStateException(
+                    "Cannot register StreamEventCallback on non-offloaded AudioTrack");
+        }
         if (executor == null) {
             throw new IllegalArgumentException("Illegal null Executor for the StreamEventCallback");
         }
         synchronized (mStreamEventCbLock) {
-            mStreamEventExec = executor;
-            mStreamEventCb = eventCallback;
+            // check if eventCallback already in list
+            for (StreamEventCbInfo seci : mStreamEventCbInfoList) {
+                if (seci.mStreamEventCb == eventCallback) {
+                    throw new IllegalArgumentException(
+                            "StreamEventCallback already registered");
+                }
+            }
+            beginStreamEventHandling();
+            mStreamEventCbInfoList.add(new StreamEventCbInfo(executor, eventCallback));
         }
     }
 
     /**
-     * @hide
-     * Unregisters the callback for notification of stream events, previously set
-     * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
+     * Unregisters the callback for notification of stream events, previously registered
+     * with {@link #registerStreamEventCallback(Executor, StreamEventCallback)}.
+     * @param eventCallback the callback to unregister.
      */
-    public void removeStreamEventCallback() {
+    public void unregisterStreamEventCallback(@NonNull StreamEventCallback eventCallback) {
+        if (eventCallback == null) {
+            throw new IllegalArgumentException("Illegal null StreamEventCallback");
+        }
+        if (!mOffloaded) {
+            throw new IllegalStateException("No StreamEventCallback on non-offloaded AudioTrack");
+        }
         synchronized (mStreamEventCbLock) {
-            mStreamEventExec = null;
-            mStreamEventCb = null;
+            StreamEventCbInfo seciToRemove = null;
+            for (StreamEventCbInfo seci : mStreamEventCbInfoList) {
+                if (seci.mStreamEventCb == eventCallback) {
+                    // ok to remove while iterating over list as we exit iteration
+                    mStreamEventCbInfoList.remove(seci);
+                    if (mStreamEventCbInfoList.size() == 0) {
+                        endStreamEventHandling();
+                    }
+                    return;
+                }
+            }
+            throw new IllegalArgumentException("StreamEventCallback was not registered");
+        }
+    }
+
+    //---------------------------------------------------------
+    // Offload
+    //--------------------
+    private static class StreamEventCbInfo {
+        final Executor mStreamEventExec;
+        final StreamEventCallback mStreamEventCb;
+
+        StreamEventCbInfo(Executor e, StreamEventCallback cb) {
+            mStreamEventExec = e;
+            mStreamEventCb = cb;
+        }
+    }
+
+    private final Object mStreamEventCbLock = new Object();
+    @GuardedBy("mStreamEventCbLock")
+    @NonNull private LinkedList<StreamEventCbInfo> mStreamEventCbInfoList =
+            new LinkedList<StreamEventCbInfo>();
+    /**
+     * Dedicated thread for handling the StreamEvent callbacks
+     */
+    private @Nullable HandlerThread mStreamEventHandlerThread;
+    private @Nullable volatile StreamEventHandler mStreamEventHandler;
+
+    /**
+     * Called from native AudioTrack callback thread, filter messages if necessary
+     * and repost event on AudioTrack message loop to prevent blocking native thread.
+     * @param what event code received from native
+     * @param arg optional argument for event
+     */
+    void handleStreamEventFromNative(int what, int arg) {
+        if (mStreamEventHandler == null) {
+            return;
+        }
+        switch (what) {
+            case NATIVE_EVENT_CAN_WRITE_MORE_DATA:
+                // replace previous CAN_WRITE_MORE_DATA messages with the latest value
+                mStreamEventHandler.removeMessages(NATIVE_EVENT_CAN_WRITE_MORE_DATA);
+                mStreamEventHandler.sendMessage(
+                        mStreamEventHandler.obtainMessage(
+                                NATIVE_EVENT_CAN_WRITE_MORE_DATA, arg, 0/*ignored*/));
+                break;
+            case NATIVE_EVENT_NEW_IAUDIOTRACK:
+                mStreamEventHandler.sendMessage(
+                        mStreamEventHandler.obtainMessage(NATIVE_EVENT_NEW_IAUDIOTRACK));
+                break;
+            case NATIVE_EVENT_STREAM_END:
+                mStreamEventHandler.sendMessage(
+                        mStreamEventHandler.obtainMessage(NATIVE_EVENT_STREAM_END));
+                break;
+        }
+    }
+
+    private class StreamEventHandler extends Handler {
+
+        StreamEventHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final LinkedList<StreamEventCbInfo> cbInfoList;
+            synchronized (mStreamEventCbLock) {
+                if (mStreamEventCbInfoList.size() == 0) {
+                    return;
+                }
+                cbInfoList = new LinkedList<StreamEventCbInfo>(mStreamEventCbInfoList);
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                for (StreamEventCbInfo cbi : cbInfoList) {
+                    switch (msg.what) {
+                        case NATIVE_EVENT_CAN_WRITE_MORE_DATA:
+                            cbi.mStreamEventExec.execute(() ->
+                                    cbi.mStreamEventCb.onDataRequest(AudioTrack.this, msg.arg1));
+                            break;
+                        case NATIVE_EVENT_NEW_IAUDIOTRACK:
+                            // TODO also release track as it's not longer usable
+                            cbi.mStreamEventExec.execute(() ->
+                                    cbi.mStreamEventCb.onTearDown(AudioTrack.this));
+                            break;
+                        case NATIVE_EVENT_STREAM_END:
+                            cbi.mStreamEventExec.execute(() ->
+                                    cbi.mStreamEventCb.onPresentationEnded(AudioTrack.this));
+                            break;
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @GuardedBy("mStreamEventCbLock")
+    private void beginStreamEventHandling() {
+        if (mStreamEventHandlerThread == null) {
+            mStreamEventHandlerThread = new HandlerThread(TAG + ".StreamEvent");
+            mStreamEventHandlerThread.start();
+            final Looper looper = mStreamEventHandlerThread.getLooper();
+            if (looper != null) {
+                mStreamEventHandler = new StreamEventHandler(looper);
+            }
+        }
+    }
+
+    @GuardedBy("mStreamEventCbLock")
+    private void endStreamEventHandling() {
+        if (mStreamEventHandlerThread != null) {
+            mStreamEventHandlerThread.quit();
+            mStreamEventHandlerThread = null;
         }
     }
 
@@ -3135,7 +3278,7 @@
     private static void postEventFromNative(Object audiotrack_ref,
             int what, int arg1, int arg2, Object obj) {
         //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
-        final AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
+        final AudioTrack track = (AudioTrack) ((WeakReference) audiotrack_ref).get();
         if (track == null) {
             return;
         }
@@ -3145,29 +3288,11 @@
             return;
         }
 
-        if (what == NATIVE_EVENT_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK
+        if (what == NATIVE_EVENT_CAN_WRITE_MORE_DATA
+                || what == NATIVE_EVENT_NEW_IAUDIOTRACK
                 || what == NATIVE_EVENT_STREAM_END) {
-            final Executor exec;
-            final StreamEventCallback cb;
-            synchronized (track.mStreamEventCbLock) {
-                exec = track.mStreamEventExec;
-                cb = track.mStreamEventCb;
-            }
-            if ((exec == null) || (cb == null)) {
-                return;
-            }
-            switch (what) {
-                case NATIVE_EVENT_MORE_DATA:
-                    exec.execute(() -> cb.onStreamDataRequest(track));
-                    return;
-                case NATIVE_EVENT_NEW_IAUDIOTRACK:
-                    // TODO also release track as it's not longer usable
-                    exec.execute(() -> cb.onTearDown(track));
-                    return;
-                case NATIVE_EVENT_STREAM_END:
-                    exec.execute(() -> cb.onStreamPresentationEnd(track));
-                    return;
-            }
+            track.handleStreamEventFromNative(what, arg1);
+            return;
         }
 
         NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
@@ -3180,7 +3305,6 @@
         }
     }
 
-
     //---------------------------------------------------------
     // Native methods called from the Java side
     //--------------------
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index a53fa11..afc99a0 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -62,6 +62,11 @@
     // intentionally less than long.MAX_VALUE
     public static final long LONG_MAX = 0x7ffffffffffffffL;
 
+    // keep consistent with native code
+    public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
+    public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;
+
+
     private int mType = TYPE_NONE;
 
     private Media2DataSource mMedia2DataSource;
@@ -77,7 +82,7 @@
 
     private String mMediaId;
     private long mStartPositionMs = 0;
-    private long mEndPositionMs = LONG_MAX;
+    private long mEndPositionMs = LONG_MAX_TIME_MS;
 
     private DataSourceDesc() {
     }
@@ -225,7 +230,7 @@
 
         private String mMediaId;
         private long mStartPositionMs = 0;
-        private long mEndPositionMs = LONG_MAX;
+        private long mEndPositionMs = LONG_MAX_TIME_MS;
 
         /**
          * Constructs a new Builder with the defaults.
@@ -326,7 +331,7 @@
          */
         public Builder setEndPosition(long position) {
             if (position < 0) {
-                position = LONG_MAX;
+                position = LONG_MAX_TIME_MS;
             }
             mEndPositionMs = position;
             return this;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 569db16..abd6411 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -151,8 +151,6 @@
     void setWiredDeviceConnectionState(int type, int state, String address, String name,
             String caller);
 
-    void setHearingAidDeviceConnectionState(in BluetoothDevice device, int state);
-
     int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
 
     void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
@@ -210,6 +208,9 @@
 
     oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
 
+    int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
+            int state, boolean suppressNoisyIntent, int musicDevice);
+
     int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
             int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);
 
diff --git a/media/java/android/media/Media2HTTPService.java b/media/java/android/media/Media2HTTPService.java
index 957acec..0d46ce4 100644
--- a/media/java/android/media/Media2HTTPService.java
+++ b/media/java/android/media/Media2HTTPService.java
@@ -18,9 +18,6 @@
 
 import android.util.Log;
 
-import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.CookieStore;
 import java.net.HttpCookie;
 import java.util.List;
 
@@ -38,45 +35,8 @@
     public Media2HTTPConnection makeHTTPConnection() {
 
         synchronized (mCookieStoreInitialized) {
-            // Only need to do it once for all connections
-            if ( !mCookieStoreInitialized )  {
-                CookieHandler cookieHandler = CookieHandler.getDefault();
-                if (cookieHandler == null) {
-                    cookieHandler = new CookieManager();
-                    CookieHandler.setDefault(cookieHandler);
-                    Log.v(TAG, "makeHTTPConnection: CookieManager created: " + cookieHandler);
-                } else {
-                    Log.v(TAG, "makeHTTPConnection: CookieHandler (" + cookieHandler + ") exists.");
-                }
-
-                // Applying the bootstrapping cookies
-                if ( mCookies != null ) {
-                    if ( cookieHandler instanceof CookieManager ) {
-                        CookieManager cookieManager = (CookieManager)cookieHandler;
-                        CookieStore store = cookieManager.getCookieStore();
-                        for ( HttpCookie cookie : mCookies ) {
-                            try {
-                                store.add(null, cookie);
-                            } catch ( Exception e ) {
-                                Log.v(TAG, "makeHTTPConnection: CookieStore.add" + e);
-                            }
-                            //for extended debugging when needed
-                            //Log.v(TAG, "MediaHTTPConnection adding Cookie[" + cookie.getName() +
-                            //        "]: " + cookie);
-                        }
-                    } else {
-                        Log.w(TAG, "makeHTTPConnection: The installed CookieHandler is not a "
-                                + "CookieManager. Can’t add the provided cookies to the cookie "
-                                + "store.");
-                    }
-                }   // mCookies
-
-                mCookieStoreInitialized = true;
-
-                Log.v(TAG, "makeHTTPConnection(" + this + "): cookieHandler: " + cookieHandler +
-                        " Cookies: " + mCookies);
-            }   // mCookieStoreInitialized
-        }   // synchronized
+            Media2Utils.storeCookies(mCookies);
+        }
 
         return new Media2HTTPConnection();
     }
diff --git a/media/java/android/media/Media2Utils.java b/media/java/android/media/Media2Utils.java
new file mode 100644
index 0000000..066233d
--- /dev/null
+++ b/media/java/android/media/Media2Utils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.util.Log;
+
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.CookieStore;
+import java.net.HttpCookie;
+import java.util.List;
+
+/** @hide */
+public class Media2Utils {
+    private static final String TAG = "Media2Utils";
+
+    private Media2Utils() {
+    }
+
+    public static synchronized void storeCookies(List<HttpCookie> cookies) {
+        CookieHandler cookieHandler = CookieHandler.getDefault();
+        if (cookieHandler == null) {
+            cookieHandler = new CookieManager();
+            CookieHandler.setDefault(cookieHandler);
+            Log.v(TAG, "storeCookies: CookieManager created: " + cookieHandler);
+        } else {
+            Log.v(TAG, "storeCookies: CookieHandler (" + cookieHandler + ") exists.");
+        }
+
+        if (cookies != null) {
+            if (cookieHandler instanceof CookieManager) {
+                CookieManager cookieManager = (CookieManager)cookieHandler;
+                CookieStore store = cookieManager.getCookieStore();
+                for (HttpCookie cookie : cookies) {
+                    try {
+                        store.add(null, cookie);
+                    } catch (Exception e) {
+                        Log.v(TAG, "storeCookies: CookieStore.add" + cookie, e);
+                    }
+                }
+            } else {
+                Log.w(TAG, "storeCookies: The installed CookieHandler is not a CookieManager."
+                        + " Can’t add the provided cookies to the cookie store.");
+            }
+        }   // cookies
+
+        Log.v(TAG, "storeCookies: cookieHandler: " + cookieHandler + " Cookies: " + cookies);
+
+    }
+
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6d122d7..ee12b91 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1192,15 +1192,17 @@
     public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
 
     /**
-     * The maximum security level supported by the device. This is the default
-     * security level when a session is opened.
+     * Indicates that the maximum security level supported by the device should
+     * be used when opening a session. This is the default security level
+     * selected when a session is opened.
      * @hide
      */
     public static final int SECURITY_LEVEL_MAX = 6;
 
     /**
-     * The maximum security level supported by the device. This is the default
-     * security level when a session is opened.
+     * Returns a value that may be passed as a parameter to {@link #openSession(int)}
+     * requesting that the session be opened at the maximum security level of
+     * the device.
      */
     public static final int getMaxSecurityLevel() {
         return SECURITY_LEVEL_MAX;
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index db6da8c..e94413c 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -37,7 +37,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.Executor;
 
@@ -1025,50 +1024,6 @@
     public abstract MediaTimestamp getTimestamp();
 
     /**
-     * Gets the media metadata.
-     *
-     * @param update_only controls whether the full set of available
-     * metadata is returned or just the set that changed since the
-     * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
-     * #METADATA_ALL}.
-     *
-     * @param apply_filter if true only metadata that matches the
-     * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
-     * #BYPASS_METADATA_FILTER}.
-     *
-     * @return The metadata, possibly empty. null if an error occured.
-     // FIXME: unhide.
-     * {@hide}
-     */
-    public Metadata getMetadata(final boolean update_only,
-            final boolean apply_filter) {
-        return null;
-    }
-
-    /**
-     * Set a filter for the metadata update notification and update
-     * retrieval. The caller provides 2 set of metadata keys, allowed
-     * and blocked. The blocked set always takes precedence over the
-     * allowed one.
-     * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
-     * shorthands to allow/block all or no metadata.
-     *
-     * By default, there is no filter set.
-     *
-     * @param allow Is the set of metadata the client is interested
-     *              in receiving new notifications for.
-     * @param block Is the set of metadata the client is not interested
-     *              in receiving new notifications for.
-     * @return The call status code.
-     *
-     // FIXME: unhide.
-     * {@hide}
-     */
-    public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
-        return 0;
-    }
-
-    /**
      * Resets the MediaPlayer2 to its uninitialized state. After calling
      * this method, you will have to initialize it again by setting the
      * data source and calling prepare().
@@ -2266,38 +2221,4 @@
         public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
 
     }
-
-    /**
-       Constant to retrieve only the new metadata since the last
-       call.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean METADATA_UPDATE_ONLY = true;
-
-    /**
-       Constant to retrieve all the metadata.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean METADATA_ALL = false;
-
-    /**
-       Constant to enable the metadata filter during retrieval.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean APPLY_METADATA_FILTER = true;
-
-    /**
-       Constant to disable the metadata filter during retrieval.
-       // FIXME: unhide.
-       // FIXME: add link to getMetadata(boolean, boolean)
-       {@hide}
-     */
-    public static final boolean BYPASS_METADATA_FILTER = false;
-
 }
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 5604ffd..6263e5d 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -25,7 +25,6 @@
 import android.content.res.AssetFileDescriptor;
 import android.graphics.SurfaceTexture;
 import android.graphics.Rect;
-import android.media.MediaPlayer2Proto;
 import android.media.MediaPlayer2Proto.PlayerMessage;
 import android.media.MediaPlayer2Proto.Value;
 import android.net.Uri;
@@ -461,12 +460,12 @@
             @Override
             void process() {
                 mVolume = volume;
-                _setVolume(volume, volume);
+                _setVolume(volume);
             }
         });
     }
 
-    private native void _setVolume(float leftVolume, float rightVolume);
+    private native void _setVolume(float volume);
 
     /**
      * Returns the current volume of this player to this player.
@@ -683,7 +682,9 @@
             case DataSourceDesc.TYPE_CALLBACK:
                 handleDataSource(isCurrent,
                                  srcId,
-                                 dsd.getMedia2DataSource());
+                                 dsd.getMedia2DataSource(),
+                                 dsd.getStartPosition(),
+                                 dsd.getEndPosition());
                 break;
 
             case DataSourceDesc.TYPE_FD:
@@ -691,7 +692,9 @@
                                  srcId,
                                  dsd.getFileDescriptor(),
                                  dsd.getFileDescriptorOffset(),
-                                 dsd.getFileDescriptorLength());
+                                 dsd.getFileDescriptorLength(),
+                                 dsd.getStartPosition(),
+                                 dsd.getEndPosition());
                 break;
 
             case DataSourceDesc.TYPE_URI:
@@ -700,7 +703,9 @@
                                  dsd.getUriContext(),
                                  dsd.getUri(),
                                  dsd.getUriHeaders(),
-                                 dsd.getUriCookies());
+                                 dsd.getUriCookies(),
+                                 dsd.getStartPosition(),
+                                 dsd.getEndPosition());
                 break;
 
             default:
@@ -730,67 +735,77 @@
     private void handleDataSource(
             boolean isCurrent, long srcId,
             @NonNull Context context, @NonNull Uri uri,
-            @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
+            @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies,
+            long startPos, long endPos)
             throws IOException {
-        // The context and URI usually belong to the calling user. Get a resolver for that user
-        // and strip out the userId from the URI if present.
+        // The context and URI usually belong to the calling user. Get a resolver for that user.
         final ContentResolver resolver = context.getContentResolver();
         final String scheme = uri.getScheme();
-        final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
         if (ContentResolver.SCHEME_FILE.equals(scheme)) {
-            handleDataSource(isCurrent, srcId, uri.getPath(), null, null);
+            handleDataSource(isCurrent, srcId, uri.getPath(), null, null, startPos, endPos);
             return;
         }
 
-        if (ContentResolver.SCHEME_CONTENT.equals(scheme)
-                && Settings.AUTHORITY.equals(authority)) {
-            // Try cached ringtone first since the actual provider may not be
-            // encryption aware, or it may be stored on CE media storage
-            final int type = RingtoneManager.getDefaultType(uri);
-            final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
-            final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
-            if (attemptDataSource(isCurrent, srcId, resolver, cacheUri)) {
+        final int ringToneType = RingtoneManager.getDefaultType(uri);
+        try {
+            AssetFileDescriptor afd;
+            // Try requested Uri locally first
+            if (ContentResolver.SCHEME_CONTENT.equals(scheme) && ringToneType != -1) {
+                afd = RingtoneManager.openDefaultRingtoneUri(context, uri);
+                if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) {
+                    return;
+                }
+                final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(
+                        context, ringToneType);
+                afd = resolver.openAssetFileDescriptor(actualUri, "r");
+            } else {
+                afd = resolver.openAssetFileDescriptor(uri, "r");
+            }
+            if (attemptDataSource(isCurrent, srcId, afd, startPos, endPos)) {
                 return;
             }
-            if (attemptDataSource(isCurrent, srcId, resolver, actualUri)) {
-                return;
-            }
-            handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
-        } else {
-            // Try requested Uri locally first, or fallback to media server
-            if (attemptDataSource(isCurrent, srcId, resolver, uri)) {
-                return;
-            }
-            handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies);
+        } catch (NullPointerException | SecurityException | IOException ex) {
+            Log.w(TAG, "Couldn't open " + uri + ": " + ex);
+            // Fallback to media server
         }
+        handleDataSource(isCurrent, srcId, uri.toString(), headers, cookies, startPos, endPos);
     }
 
-    private boolean attemptDataSource(
-            boolean isCurrent, long srcId, ContentResolver resolver, Uri uri) {
-        try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
+    private boolean attemptDataSource(boolean isCurrent, long srcId, AssetFileDescriptor afd,
+            long startPos, long endPos) throws IOException {
+        try {
             if (afd.getDeclaredLength() < 0) {
                 handleDataSource(isCurrent,
-                                 srcId,
-                                 afd.getFileDescriptor(),
-                                 0,
-                                 DataSourceDesc.LONG_MAX);
+                        srcId,
+                        afd.getFileDescriptor(),
+                        0,
+                        DataSourceDesc.LONG_MAX,
+                        startPos,
+                        endPos);
             } else {
                 handleDataSource(isCurrent,
-                                 srcId,
-                                 afd.getFileDescriptor(),
-                                 afd.getStartOffset(),
-                                 afd.getDeclaredLength());
+                        srcId,
+                        afd.getFileDescriptor(),
+                        afd.getStartOffset(),
+                        afd.getDeclaredLength(),
+                        startPos,
+                        endPos);
             }
             return true;
         } catch (NullPointerException | SecurityException | IOException ex) {
-            Log.w(TAG, "Couldn't open " + uri + ": " + ex);
+            Log.w(TAG, "Couldn't open srcId:" + srcId + ": " + ex);
             return false;
+        } finally {
+            if (afd != null) {
+                afd.close();
+            }
         }
     }
 
     private void handleDataSource(
             boolean isCurrent, long srcId,
-            String path, Map<String, String> headers, List<HttpCookie> cookies)
+            String path, Map<String, String> headers, List<HttpCookie> cookies,
+            long startPos, long endPos)
             throws IOException {
         String[] keys = null;
         String[] values = null;
@@ -806,11 +821,12 @@
                 ++i;
             }
         }
-        handleDataSource(isCurrent, srcId, path, keys, values, cookies);
+        handleDataSource(isCurrent, srcId, path, keys, values, cookies, startPos, endPos);
     }
 
     private void handleDataSource(boolean isCurrent, long srcId,
-            String path, String[] keys, String[] values, List<HttpCookie> cookies)
+            String path, String[] keys, String[] values, List<HttpCookie> cookies,
+            long startPos, long endPos)
             throws IOException {
         final Uri uri = Uri.parse(path);
         final String scheme = uri.getScheme();
@@ -818,13 +834,16 @@
             path = uri.getPath();
         } else if (scheme != null) {
             // handle non-file sources
+            Media2Utils.storeCookies(cookies);
             nativeHandleDataSourceUrl(
                 isCurrent,
                 srcId,
-                Media2HTTPService.createHTTPService(path, cookies),
+                Media2HTTPService.createHTTPService(path),
                 path,
                 keys,
-                values);
+                values,
+                startPos,
+                endPos);
             return;
         }
 
@@ -832,7 +851,7 @@
         if (file.exists()) {
             FileInputStream is = new FileInputStream(file);
             FileDescriptor fd = is.getFD();
-            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX);
+            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
             is.close();
         } else {
             throw new IOException("handleDataSource failed.");
@@ -841,7 +860,8 @@
 
     private native void nativeHandleDataSourceUrl(
             boolean isCurrent, long srcId,
-            Media2HTTPService httpService, String path, String[] keys, String[] values)
+            Media2HTTPService httpService, String path, String[] keys, String[] values,
+            long startPos, long endPos)
             throws IOException;
 
     /**
@@ -855,23 +875,27 @@
      */
     private void handleDataSource(
             boolean isCurrent, long srcId,
-            FileDescriptor fd, long offset, long length) throws IOException {
-        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length);
+            FileDescriptor fd, long offset, long length,
+            long startPos, long endPos) throws IOException {
+        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
     }
 
     private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
-            FileDescriptor fd, long offset, long length) throws IOException;
+            FileDescriptor fd, long offset, long length,
+            long startPos, long endPos) throws IOException;
 
     /**
      * @throws IllegalStateException if it is called in an invalid state
      * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
      */
-    private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource) {
-        nativeHandleDataSourceCallback(isCurrent, srcId, dataSource);
+    private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource,
+            long startPos, long endPos) {
+        nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
     }
 
     private native void nativeHandleDataSourceCallback(
-            boolean isCurrent, long srcId, Media2DataSource dataSource);
+            boolean isCurrent, long srcId, Media2DataSource dataSource,
+            long startPos, long endPos);
 
     /**
      * @return true if there is a next data source, false otherwise.
@@ -3253,12 +3277,12 @@
         }
 
         @Override
-        public void onStreamPresentationEnd(AudioTrack track) {
+        public void onPresentationEnded(AudioTrack track) {
             native_stream_event_onStreamPresentationEnd(mNativeCallbackPtr, mUserDataPtr);
         }
 
         @Override
-        public void onStreamDataRequest(AudioTrack track) {
+        public void onDataRequest(AudioTrack track, int size) {
             native_stream_event_onStreamDataRequest(
                     mJAudioTrackPtr, mNativeCallbackPtr, mUserDataPtr);
         }
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 66feb1d..8664aa1 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
+import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
@@ -1128,6 +1129,38 @@
     }
 
     /**
+     * Opens a raw file descriptor to read the data under the given default URI.
+     *
+     * @param context the Context to use when resolving the Uri.
+     * @param uri The desired default URI to open.
+     * @return a new AssetFileDescriptor pointing to the file. You own this descriptor
+     * and are responsible for closing it when done. This value may be {@code null}.
+     * @throws FileNotFoundException if the provided URI could not be opened.
+     * @see #getDefaultUri
+     */
+    public static AssetFileDescriptor openDefaultRingtoneUri(
+            @NonNull Context context, @NonNull Uri uri) throws FileNotFoundException {
+        // Try cached ringtone first since the actual provider may not be
+        // encryption aware, or it may be stored on CE media storage
+        final int type = getDefaultType(uri);
+        final Uri cacheUri = getCacheForType(type, context.getUserId());
+        final Uri actualUri = getActualDefaultRingtoneUri(context, type);
+        final ContentResolver resolver = context.getContentResolver();
+
+        AssetFileDescriptor afd = null;
+        if (cacheUri != null) {
+            afd = resolver.openAssetFileDescriptor(cacheUri, "r");
+            if (afd != null) {
+                return afd;
+            }
+        }
+        if (actualUri != null) {
+            afd = resolver.openAssetFileDescriptor(actualUri, "r");
+        }
+        return afd;
+    }
+
+    /**
      * Creates a {@link android.media.MediaScannerConnection} to scan a ringtone file and add its
      * information to the internal database.
      *
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 9eb0c6d..cf7bf19 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -365,4 +365,22 @@
             return Integer.MAX_VALUE;
         }
     }
+
+    /**
+     * Synchronously get state of the indicated model.  The model state is returned as
+     * a recognition event, or null if the model is not loaded, or if this method
+     * is not supported.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    public SoundTrigger.RecognitionEvent getModelState(UUID soundModelId) {
+        if (soundModelId == null) {
+            return null;
+        }
+        try {
+            return mSoundTriggerService.getModelState(new ParcelUuid(soundModelId));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/media/java/android/media/update/ApiLoader.java b/media/java/android/media/update/ApiLoader.java
index a7eb30d..0c1d1a2 100644
--- a/media/java/android/media/update/ApiLoader.java
+++ b/media/java/android/media/update/ApiLoader.java
@@ -16,14 +16,64 @@
 
 package android.media.update;
 
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.GuardedBy;
+
+import dalvik.system.PathClassLoader;
+
+import java.io.File;
+
 /**
  * @hide
  */
 public final class ApiLoader {
+    @GuardedBy("this")
+    private static StaticProvider sMediaUpdatable;
+
+    private static final String UPDATE_PACKAGE = "com.android.media.update";
+    private static final String UPDATE_CLASS = "com.android.media.update.ApiFactory";
+    private static final String UPDATE_METHOD = "initialize";
+    private static final boolean REGISTER_UPDATE_DEPENDENCY = true;
+
     private ApiLoader() { }
 
     public static StaticProvider getProvider() {
-        throw new RuntimeException("Use MediaSession/Browser instead of"
-                + " hidden MediaSession2/Browser2 APIs.");
+        try {
+            return getMediaUpdatable();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (NameNotFoundException | ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // TODO This method may do I/O; Ensure it does not violate (emit warnings in) strict mode.
+    private static synchronized StaticProvider getMediaUpdatable()
+            throws NameNotFoundException, ReflectiveOperationException, RemoteException {
+        if (sMediaUpdatable != null) return sMediaUpdatable;
+
+        // TODO Figure out when to use which package (query media update service)
+        int flags = Build.IS_DEBUGGABLE ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
+        ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+                UPDATE_PACKAGE, flags, UserHandle.myUserId());
+
+        if (REGISTER_UPDATE_DEPENDENCY) {
+            // Register a dependency to the updatable in order to be killed during updates
+            ActivityManager.getService().addPackageDependency(ai.packageName);
+        }
+
+        ClassLoader classLoader = new PathClassLoader(ai.sourceDir,
+                ai.nativeLibraryDir + File.pathSeparator + System.getProperty("java.library.path"),
+                ClassLoader.getSystemClassLoader().getParent());
+        return sMediaUpdatable = (StaticProvider) classLoader.loadClass(UPDATE_CLASS)
+                .getMethod(UPDATE_METHOD, ApplicationInfo.class).invoke(null, ai);
     }
 }
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index e67b100..474b671 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -538,8 +538,13 @@
         MtpPropertyGroup propertyGroup;
         for (MtpStorageManager.MtpObject obj : objs) {
             if (property == 0xffffffff) {
+                if (format == 0 && handle != 0 && handle != 0xffffffff) {
+                    // return properties based on the object's format
+                    format = obj.getFormat();
+                }
                 // Get all properties supported by this object
-                propertyGroup = mPropertyGroupsByFormat.get(obj.getFormat());
+                // format should be the same between get & put
+                propertyGroup = mPropertyGroupsByFormat.get(format);
                 if (propertyGroup == null) {
                     int[] propertyList = getSupportedObjectProperties(format);
                     propertyGroup = new MtpPropertyGroup(mMediaProvider, mVolumeName,
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 3870124..fa9ab1f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -128,6 +128,7 @@
         "libmediametrics",
         "libmediaplayer2",
         "libmediaplayer2-protos",
+        "libmediandk_utils",
         "libmediautils",
         "libnetd_client",  // for setNetworkForUser
         "libprotobuf-cpp-lite",
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index b52da36..61c28ed 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -283,7 +283,8 @@
 static void
 android_media_MediaPlayer2_handleDataSourceUrl(
         JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
-        jobject httpServiceObj, jstring path, jobjectArray keys, jobjectArray values) {
+        jobject httpServiceObj, jstring path, jobjectArray keys, jobjectArray values,
+        jlong startPos, jlong endPos) {
 
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL) {
@@ -300,7 +301,8 @@
     if (tmp == NULL) {  // Out of memory
         return;
     }
-    ALOGV("handleDataSourceUrl: path %s, srcId %lld", tmp, (long long)srcId);
+    ALOGV("handleDataSourceUrl: path %s, srcId %lld, start %lld, end %lld",
+          tmp, (long long)srcId, (long long)startPos, (long long)endPos);
 
     if (strncmp(tmp, "content://", 10) == 0) {
         ALOGE("handleDataSourceUrl: content scheme is not supported in native code");
@@ -313,6 +315,8 @@
     dsd->mId = srcId;
     dsd->mType = DataSourceDesc::TYPE_URL;
     dsd->mUrl = tmp;
+    dsd->mStartPositionMs = startPos;
+    dsd->mEndPositionMs = endPos;
 
     env->ReleaseStringUTFChars(path, tmp);
     tmp = NULL;
@@ -341,9 +345,9 @@
 
 static void
 android_media_MediaPlayer2_handleDataSourceFD(
-    JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
-    jobject fileDescriptor, jlong offset, jlong length)
-{
+        JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId,
+        jobject fileDescriptor, jlong offset, jlong length,
+        jlong startPos, jlong endPos) {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
@@ -355,8 +359,10 @@
         return;
     }
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-    ALOGV("handleDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld",
-          (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
+    ALOGV("handleDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld, "
+          "start=%lld, end=%lld",
+          (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length,
+          (long long)startPos, (long long)endPos);
 
     struct stat sb;
     int ret = fstat(fd, &sb);
@@ -389,6 +395,8 @@
     dsd->mFD = fd;
     dsd->mFDOffset = offset;
     dsd->mFDLength = length;
+    dsd->mStartPositionMs = startPos;
+    dsd->mEndPositionMs = endPos;
 
     status_t err;
     if (isCurrent) {
@@ -402,7 +410,8 @@
 
 static void
 android_media_MediaPlayer2_handleDataSourceCallback(
-    JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId, jobject dataSource)
+    JNIEnv *env, jobject thiz, jboolean isCurrent, jlong srcId, jobject dataSource,
+    jlong startPos, jlong endPos)
 {
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -419,6 +428,8 @@
     dsd->mId = srcId;
     dsd->mType = DataSourceDesc::TYPE_CALLBACK;
     dsd->mCallbackSource = callbackDataSource;
+    dsd->mStartPositionMs = startPos;
+    dsd->mEndPositionMs = endPos;
 
     status_t err;
     if (isCurrent) {
@@ -974,15 +985,15 @@
 }
 
 static void
-android_media_MediaPlayer2_setVolume(JNIEnv *env, jobject thiz, jfloat leftVolume, jfloat rightVolume)
+android_media_MediaPlayer2_setVolume(JNIEnv *env, jobject thiz, jfloat volume)
 {
-    ALOGV("setVolume: left %f  right %f", (float) leftVolume, (float) rightVolume);
+    ALOGV("setVolume: volume %f", (float) volume);
     sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
         jniThrowException(env, "java/lang/IllegalStateException", NULL);
         return;
     }
-    process_media_player_call( env, thiz, mp->setVolume((float) leftVolume, (float) rightVolume), NULL, NULL );
+    process_media_player_call( env, thiz, mp->setVolume((float) volume), NULL, NULL );
 }
 
 static jbyteArray
@@ -1442,17 +1453,17 @@
     {
         "nativeHandleDataSourceUrl",
         "(ZJLandroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
-        "[Ljava/lang/String;)V",
+        "[Ljava/lang/String;JJ)V",
         (void *)android_media_MediaPlayer2_handleDataSourceUrl
     },
     {
         "nativeHandleDataSourceFD",
-        "(ZJLjava/io/FileDescriptor;JJ)V",
+        "(ZJLjava/io/FileDescriptor;JJJJ)V",
         (void *)android_media_MediaPlayer2_handleDataSourceFD
     },
     {
         "nativeHandleDataSourceCallback",
-        "(ZJLandroid/media/Media2DataSource;)V",
+        "(ZJLandroid/media/Media2DataSource;JJ)V",
         (void *)android_media_MediaPlayer2_handleDataSourceCallback
     },
     {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
@@ -1481,7 +1492,7 @@
     {"getParameter",        "(I)Ljava/lang/Object;",           (void *)android_media_MediaPlayer2_getParameter},
     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer2_setLooping},
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer2_isLooping},
-    {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer2_setVolume},
+    {"_setVolume",          "(F)V",                             (void *)android_media_MediaPlayer2_setVolume},
     {"_invoke",             "([B)[B",                           (void *)android_media_MediaPlayer2_invoke},
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer2_native_init},
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer2_native_setup},
diff --git a/opengl/java/android/opengl/EGL15.java b/opengl/java/android/opengl/EGL15.java
new file mode 100644
index 0000000..f855fe2
--- /dev/null
+++ b/opengl/java/android/opengl/EGL15.java
@@ -0,0 +1,167 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * EGL 1.5
+ *
+ */
+public class EGL15 {
+
+    public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT            = 0x00000001;
+    public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT   = 0x00000002;
+    public static final int EGL_OPENGL_ES3_BIT                             = 0x00000040;
+    public static final int EGL_SYNC_FLUSH_COMMANDS_BIT                    = 0x0001;
+    public static final int EGL_GL_COLORSPACE_SRGB                         = 0x3089;
+    public static final int EGL_GL_COLORSPACE_LINEAR                       = 0x308A;
+    public static final int EGL_CONTEXT_MAJOR_VERSION                      = 0x3098;
+    public static final int EGL_CL_EVENT_HANDLE                            = 0x309C;
+    public static final int EGL_GL_COLORSPACE                              = 0x309D;
+    public static final int EGL_GL_TEXTURE_2D                              = 0x30B1;
+    public static final int EGL_GL_TEXTURE_3D                              = 0x30B2;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X             = 0x30B3;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X             = 0x30B4;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y             = 0x30B5;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y             = 0x30B6;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z             = 0x30B7;
+    public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z             = 0x30B8;
+    public static final int EGL_GL_RENDERBUFFER                            = 0x30B9;
+    public static final int EGL_GL_TEXTURE_LEVEL                           = 0x30BC;
+    public static final int EGL_GL_TEXTURE_ZOFFSET                         = 0x30BD;
+    public static final int EGL_IMAGE_PRESERVED                            = 0x30D2;
+    public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE               = 0x30F0;
+    public static final int EGL_SYNC_STATUS                                = 0x30F1;
+    public static final int EGL_SIGNALED                                   = 0x30F2;
+    public static final int EGL_UNSIGNALED                                 = 0x30F3;
+    public static final int EGL_TIMEOUT_EXPIRED                            = 0x30F5;
+    public static final int EGL_CONDITION_SATISFIED                        = 0x30F6;
+    public static final int EGL_SYNC_TYPE                                  = 0x30F7;
+    public static final int EGL_SYNC_CONDITION                             = 0x30F8;
+    public static final int EGL_SYNC_FENCE                                 = 0x30F9;
+    public static final int EGL_CONTEXT_MINOR_VERSION                      = 0x30FB;
+    public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK                = 0x30FD;
+    public static final int EGL_SYNC_CL_EVENT                              = 0x30FE;
+    public static final int EGL_SYNC_CL_EVENT_COMPLETE                     = 0x30FF;
+    public static final int EGL_CONTEXT_OPENGL_DEBUG                       = 0x31B0;
+    public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE          = 0x31B1;
+    public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS               = 0x31B2;
+    public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD;
+    public static final int EGL_NO_RESET_NOTIFICATION                      = 0x31BE;
+    public static final int EGL_LOSE_CONTEXT_ON_RESET                      = 0x31BF;
+    public static final int EGL_PLATFORM_ANDROID_KHR                       = 0x3141;
+    public static final long EGL_FOREVER                                   = 0xFFFFFFFFFFFFFFFFL;
+    public static final EGLImage EGL_NO_IMAGE                              = null;
+    public static final EGLSync EGL_NO_SYNC                                = null;
+    public static final EGLContext EGL_NO_CONTEXT                          = null;
+    public static final EGLDisplay EGL_NO_DISPLAY                          = null;
+    public static final EGLSurface EGL_NO_SURFACE                          = null;
+
+    native private static void _nativeClassInit();
+    static {
+        _nativeClassInit();
+    }
+    // C function EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list )
+
+    public static native EGLSync eglCreateSync(
+        EGLDisplay dpy,
+        int type,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync )
+
+    public static native boolean eglDestroySync(
+        EGLDisplay dpy,
+        EGLSync sync
+    );
+
+    // C function EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout )
+
+    public static native int eglClientWaitSync(
+        EGLDisplay dpy,
+        EGLSync sync,
+        int flags,
+        long timeout
+    );
+
+    // C function EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value )
+
+    public static native boolean eglGetSyncAttrib(
+        EGLDisplay dpy,
+        EGLSync sync,
+        int attribute,
+        long[] value,
+        int offset
+    );
+
+    // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list )
+
+    public static native EGLDisplay eglGetPlatformDisplay(
+        int platform,
+        long native_display,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list )
+
+    public static native EGLSurface eglCreatePlatformWindowSurface(
+        EGLDisplay dpy,
+        EGLConfig config,
+        java.nio.Buffer native_window,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list )
+
+    public static native EGLSurface eglCreatePlatformPixmapSurface(
+        EGLDisplay dpy,
+        EGLConfig config,
+        java.nio.Buffer native_pixmap,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags )
+
+    public static native boolean eglWaitSync(
+        EGLDisplay dpy,
+        EGLSync sync,
+        int flags
+    );
+
+    // C function EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list )
+
+    public static native EGLImage eglCreateImage(
+        EGLDisplay dpy,
+        EGLContext context,
+        int target,
+        long buffer,
+        long[] attrib_list,
+        int offset
+    );
+
+    // C function EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image )
+
+    public static native boolean eglDestroyImage(
+        EGLDisplay dpy,
+        EGLImage image
+    );
+
+}
diff --git a/opengl/java/android/opengl/EGLImage.java b/opengl/java/android/opengl/EGLImage.java
new file mode 100644
index 0000000..731ce72
--- /dev/null
+++ b/opengl/java/android/opengl/EGLImage.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLImage objects.
+ *
+ */
+public class EGLImage extends EGLObjectHandle {
+    private EGLImage(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLImage)) return false;
+
+        EGLImage that = (EGLImage) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/opengl/java/android/opengl/EGLSync.java b/opengl/java/android/opengl/EGLSync.java
new file mode 100644
index 0000000..472f9e7
--- /dev/null
+++ b/opengl/java/android/opengl/EGLSync.java
@@ -0,0 +1,37 @@
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.opengl;
+
+/**
+ * Wrapper class for native EGLSync objects.
+ *
+ */
+public class EGLSync extends EGLObjectHandle {
+    private EGLSync(long handle) {
+        super(handle);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof EGLSync)) return false;
+
+        EGLSync that = (EGLSync) o;
+        return getNativeHandle() == that.getNativeHandle();
+    }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 33cb5964..4518d79 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -30,7 +30,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.carrierdefaultapp.R;
+
 /**
  * This util class provides common logic for carrier actions
  */
@@ -102,7 +102,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onDisableAllMeteredApns subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, !ENABLE);
+        telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(!ENABLE);
     }
 
     private static void onEnableAllMeteredApns(Intent intent, Context context) {
@@ -110,7 +110,7 @@
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
         logd("onEnableAllMeteredApns subId: " + subId);
         final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
-        telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, ENABLE);
+        telephonyMgr.createForSubscriptionId(subId).setCarrierDataEnabled(ENABLE);
     }
 
     private static void onEnableDefaultURLHandler(Context context) {
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
index f9dbcd4..5d84d64 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/CarrierDefaultReceiverTest.java
@@ -104,6 +104,6 @@
         assertNotNull(pendingIntent);
 
         Rlog.d(TAG, "verify carrier action: disable all metered apns");
-        verify(mTelephonyMgr).carrierActionSetMeteredApnsEnabled(eq(subId), eq(false));
+        verify(mTelephonyMgr).setCarrierDataEnabled(eq(false));
     }
 }
diff --git a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java b/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
index 5782ea1..31c9224 100644
--- a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
@@ -66,7 +66,7 @@
 
     public void onNotificationSeen(NotificationEntry entry) {
         // user has strong opinions about this notification. we can't down rank it, so don't bother.
-        if (entry.getChannel().isImportanceLocked()) {
+        if (entry.getChannel().hasUserSetImportance()) {
             return;
         }
 
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 3333e15..8f33a70 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -25,7 +25,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
-import android.app.AlarmManager;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -224,8 +223,9 @@
     }
 
     /** A convenience helper for creating an adjustment for an SBN. */
+    @VisibleForTesting
     @Nullable
-    private Adjustment createEnqueuedNotificationAdjustment(
+    Adjustment createEnqueuedNotificationAdjustment(
             @NonNull NotificationEntry entry,
             @NonNull ArrayList<Notification.Action> smartActions,
             @NonNull ArrayList<CharSequence> smartReplies) {
@@ -237,7 +237,9 @@
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
         if (mNotificationCategorizer.shouldSilence(entry)) {
-            signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
+            final int importance = entry.getImportance() < IMPORTANCE_LOW ? entry.getImportance()
+                    : IMPORTANCE_LOW;
+            signals.putInt(KEY_IMPORTANCE, importance);
         }
 
         return new Adjustment(
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
index 2820232..2eb005a 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -66,6 +66,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
+import java.util.ArrayList;
 
 public class AssistantTest extends ServiceTestCase<Assistant> {
 
@@ -466,4 +467,12 @@
 
         assertFalse(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
     }
+
+    @Test
+    public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
+        Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(new NotificationEntry(
+                mPackageManager, sbn, P1C3), new ArrayList<>(), new ArrayList<>());
+        assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
+    }
 }
diff --git a/packages/ExternalStorageProvider/Android.bp b/packages/ExternalStorageProvider/Android.bp
new file mode 100644
index 0000000..973fef3
--- /dev/null
+++ b/packages/ExternalStorageProvider/Android.bp
@@ -0,0 +1,19 @@
+android_app {
+    name: "ExternalStorageProvider",
+
+    manifest: "AndroidManifest.xml",
+
+    resource_dirs: [
+        "res",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    platform_apis: true,
+
+    certificate: "platform",
+
+    privileged: true,
+}
diff --git a/packages/ExternalStorageProvider/Android.mk b/packages/ExternalStorageProvider/Android.mk
deleted file mode 100644
index 9e99313..0000000
--- a/packages/ExternalStorageProvider/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := ExternalStorageProvider
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/AndroidManifest.xml b/packages/ExternalStorageProvider/AndroidManifest.xml
index 1072f95..484dbcc 100644
--- a/packages/ExternalStorageProvider/AndroidManifest.xml
+++ b/packages/ExternalStorageProvider/AndroidManifest.xml
@@ -17,6 +17,10 @@
             <intent-filter>
                 <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
             </intent-filter>
+            <!-- Stub that allows MediaProvider to make incoming calls -->
+            <path-permission
+                android:path="/media_internal"
+                android:permission="android.permission.WRITE_MEDIA_STORAGE" />
         </provider>
 
         <receiver android:name=".MountReceiver">
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 62207c5..4e52ff6d 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -37,6 +37,7 @@
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Path;
 import android.provider.DocumentsContract.Root;
+import android.provider.MediaStore;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -606,11 +607,16 @@
                     }
                     break;
                 }
-                case "getDocumentId": {
-                    final String path = arg;
-                    final List<UriPermission> accessUriPermissions =
-                            extras.getParcelableArrayList(AUTHORITY + ".extra.uriPermissions");
+                case MediaStore.GET_DOCUMENT_URI_CALL: {
+                    // All callers must go through MediaProvider
+                    getContext().enforceCallingPermission(
+                            android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);
 
+                    final Uri fileUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
+                    final List<UriPermission> accessUriPermissions = extras
+                            .getParcelableArrayList(DocumentsContract.EXTRA_URI_PERMISSIONS);
+
+                    final String path = fileUri.getPath();
                     try {
                         final Bundle out = new Bundle();
                         final Uri uri = getDocumentUri(path, accessUriPermissions);
@@ -619,7 +625,22 @@
                     } catch (FileNotFoundException e) {
                         throw new IllegalStateException("File in " + path + " is not found.", e);
                     }
+                }
+                case MediaStore.GET_MEDIA_URI_CALL: {
+                    // All callers must go through MediaProvider
+                    getContext().enforceCallingPermission(
+                            android.Manifest.permission.WRITE_MEDIA_STORAGE, TAG);
 
+                    final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
+                    final String docId = DocumentsContract.getDocumentId(documentUri);
+                    try {
+                        final Bundle out = new Bundle();
+                        final Uri uri = Uri.fromFile(getFileForDocId(docId));
+                        out.putParcelable(DocumentsContract.EXTRA_URI, uri);
+                        return out;
+                    } catch (FileNotFoundException e) {
+                        throw new IllegalStateException(e);
+                    }
                 }
                 default:
                     Log.w(TAG, "unknown method passed to call(): " + method);
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
new file mode 100644
index 0000000..83427d4
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -0,0 +1,25 @@
+android_test {
+    name: "ExternalStorageProviderTests",
+
+    manifest: "AndroidManifest.xml",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    libs: [
+        "android.test.base",
+        "android.test.mock",
+        "android.test.runner",
+    ],
+
+    static_libs: [
+        "android-support-test",
+        "mockito-target",
+        "truth-prebuilt",
+    ],
+
+    certificate: "platform",
+
+    instrumentation_for: "ExternalStorageProvider",
+}
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
new file mode 100644
index 0000000..58b6e86
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.externalstorage.tests">
+
+    <application android:label="ExternalStorageProvider Tests">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.externalstorage"
+                     android:label="ExternalStorageProvider Tests" />
+</manifest>
+
diff --git a/packages/ExternalStorageProvider/tests/AndroidTest.xml b/packages/ExternalStorageProvider/tests/AndroidTest.xml
new file mode 100644
index 0000000..e5fa73f
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Tests for ExternalStorageProvider.">
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="ExternalStorageProviderTests.apk" />
+    </target_preparer>
+
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="framework-base-presubmit" />
+    <option name="test-tag" value="ExternalStorageProviderTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.externalstorage.tests" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
new file mode 100644
index 0000000..a88b3e1
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.externalstorage;
+
+import static com.android.externalstorage.ExternalStorageProvider.AUTHORITY;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.pm.ProviderInfo;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ExternalStorageProviderTest {
+    @Test
+    public void onCreate_shouldUpdateVolumes() throws Exception {
+        ExternalStorageProvider externalStorageProvider = new ExternalStorageProvider();
+        ExternalStorageProvider spyProvider = spy(externalStorageProvider);
+        ProviderInfo providerInfo = new ProviderInfo();
+        providerInfo.authority = AUTHORITY;
+        providerInfo.grantUriPermissions = true;
+        providerInfo.exported = true;
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                spyProvider.attachInfoForTesting(
+                        InstrumentationRegistry.getTargetContext(), providerInfo);
+            }
+        });
+
+        verify(spyProvider, atLeast(1)).updateVolumes();
+    }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 8fed367..f2de9ec 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -54,9 +54,14 @@
         Intent intent = getIntent();
         String callingPackage = getCallingPackage();
 
+        final boolean isSessionInstall =
+                PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
+
         // If the activity was started via a PackageInstaller session, we retrieve the calling
         // package from that session
-        int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
+        final int sessionId = (isSessionInstall
+                ? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
+                : -1);
         if (callingPackage == null && sessionId != -1) {
             PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
             PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
@@ -99,7 +104,7 @@
         nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
         nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
 
-        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
+        if (isSessionInstall) {
             nextActivity.setClass(this, PackageInstallerActivity.class);
         } else {
             Uri packageUri = intent.getData();
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index d60dbe7..5161344 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -2,16 +2,14 @@
 
     name: "SettingsLib",
 
-    libs: [
+    static_libs: [
         "androidx.annotation_annotation",
         "androidx.legacy_legacy-support-v4",
         "androidx.recyclerview_recyclerview",
         "androidx.preference_preference",
         "androidx.appcompat_appcompat",
         "androidx.lifecycle_lifecycle-runtime",
-    ],
 
-    static_libs: [
         "SettingsLibHelpUtils",
         "SettingsLibRestrictedLockUtils",
         "SettingsLibAppPreference",
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index e278c10..8529e3e 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -80,11 +80,10 @@
             if (admin.component != null) {
                 intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
             }
-            int adminUserId = UserHandle.myUserId();
-            if (admin.user != null) {
-                adminUserId = admin.user.getIdentifier();
-            }
-            intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
+            final UserHandle adminUser = admin.user != null
+                    ? admin.user
+                    : UserHandle.of(UserHandle.myUserId());
+            intent.putExtra(Intent.EXTRA_USER, adminUser);
         }
         return intent;
     }
diff --git a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
index af30425..cbebbb3 100644
--- a/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/drawable/settings_spinner_background.xml
@@ -18,11 +18,14 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
             android:paddingMode="stack">
     <item>
-        <shape>
+        <shape
+            android:tint="?android:attr/colorForeground">
             <corners
                 android:radius="20dp"/>
+            <solid
+                android:color="@android:color/transparent"/>
             <stroke
-                android:color="?android:attr/textColorSecondary"
+                android:color="#1f000000"
                 android:width="1dp"/>
             <size
                 android:height="32dp"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
index 3102239..3c45112 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -25,7 +25,6 @@
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.util.IconDrawableFactory;
 
 import com.android.settingslib.widget.CandidateInfo;
@@ -46,8 +45,8 @@
         this(context, pm, uid, cn, null /* summary */, true /* enabled */);
     }
 
-    public DefaultAppInfo(Context context, PackageManager pm, PackageItemInfo info) {
-        this(context, pm, info, null /* summary */, true /* enabled */);
+    public DefaultAppInfo(Context context, PackageManager pm, int uid, PackageItemInfo info) {
+        this(context, pm, uid, info, null /* summary */, true /* enabled */);
     }
 
     public DefaultAppInfo(Context context, PackageManager pm, int uid, ComponentName cn,
@@ -61,12 +60,12 @@
         this.summary = summary;
     }
 
-    public DefaultAppInfo(Context context, PackageManager pm, PackageItemInfo info,
+    public DefaultAppInfo(Context context, PackageManager pm, int uid, PackageItemInfo info,
                           String summary, boolean enabled) {
         super(enabled);
         mContext = context;
         mPm = pm;
-        userId = UserHandle.myUserId();
+        userId = uid;
         packageItemInfo = info;
         componentName = null;
         this.summary = summary;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 750a843..a2e30df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -60,11 +60,11 @@
     private short mRssi;
 
     private final List<LocalBluetoothProfile> mProfiles =
-            new ArrayList<LocalBluetoothProfile>();
+            Collections.synchronizedList(new ArrayList<>());
 
     // List of profiles that were previously in mProfiles, but have been removed
     private final List<LocalBluetoothProfile> mRemovedProfiles =
-            new ArrayList<LocalBluetoothProfile>();
+            Collections.synchronizedList(new ArrayList<>());
 
     // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
     private boolean mLocalNapRoleConnected;
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
index 78963f3..027ca09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
@@ -102,8 +102,4 @@
     public void count(Context context, String name, int value) {
         MetricsLogger.count(context, name, value);
     }
-
-    public void histogram(Context context, String name, int bucket) {
-        MetricsLogger.histogram(context, name, bucket);
-    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
index 4b9f572..379a820 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
@@ -76,9 +76,4 @@
      * Logs a count.
      */
     void count(Context context, String name, int value);
-
-    /**
-     * Logs a histogram event.
-     */
-    void histogram(Context context, String name, int bucket);
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 1e5b378..662fa10 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -59,44 +59,18 @@
         }
     }
 
-    /**
-     * Logs a user action. Includes the elapsed time since the containing
-     * fragment has been visible.
-     */
-    public void action(VisibilityLoggerMixin visibilityLogger, int category, int value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(category, value,
-                    sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
-        }
-    }
-
-    /**
-     * Logs a user action. Includes the elapsed time since the containing
-     * fragment has been visible.
-     */
-    public void action(VisibilityLoggerMixin visibilityLogger, int category, boolean value) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.action(category, value,
-                    sinceVisibleTaggedData(visibilityLogger.elapsedTimeSinceVisible()));
-        }
-    }
-
     public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
         for (LogWriter writer : mLoggerWriters) {
             writer.action(context, category, taggedData);
         }
     }
 
-    /** @deprecated use {@link #action(VisibilityLoggerMixin, int, int)} */
-    @Deprecated
     public void action(Context context, int category, int value) {
         for (LogWriter writer : mLoggerWriters) {
             writer.action(context, category, value);
         }
     }
 
-    /** @deprecated use {@link #action(VisibilityLoggerMixin, int, boolean)} */
-    @Deprecated
     public void action(Context context, int category, boolean value) {
         for (LogWriter writer : mLoggerWriters) {
             writer.action(context, category, value);
@@ -116,12 +90,6 @@
         }
     }
 
-    public void histogram(Context context, String name, int bucket) {
-        for (LogWriter writer : mLoggerWriters) {
-            writer.histogram(context, name, bucket);
-        }
-    }
-
     public int getMetricsCategory(Object object) {
         if (object == null || !(object instanceof Instrumentable)) {
             return MetricsEvent.VIEW_UNKNOWN;
@@ -153,7 +121,4 @@
                 Pair.create(MetricsEvent.FIELD_CONTEXT, sourceMetricsCategory));
     }
 
-    private Pair<Integer, Object> sinceVisibleTaggedData(long timestamp) {
-        return Pair.create(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS, timestamp);
-    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
index 01f0d78..a92a2dd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -72,7 +72,7 @@
     @Test
     public void initInfoWithActivityInfo_shouldLoadInfo() {
         mPackageItemInfo.packageName = "test";
-        mInfo = new DefaultAppInfo(mContext, mPackageManager, mPackageItemInfo);
+        mInfo = new DefaultAppInfo(mContext, mPackageManager, 0 /* uid */, mPackageItemInfo);
         mInfo.loadLabel();
         Drawable icon = mInfo.loadIcon();
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index f4fd779..7a7f0d4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -15,13 +15,10 @@
  */
 package com.android.settingslib.core.instrumentation;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -34,8 +31,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
@@ -46,30 +41,20 @@
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class MetricsFeatureProviderTest {
-    private static int CATEGORY = 10;
-    private static boolean SUBTYPE_BOOLEAN = true;
-    private static int SUBTYPE_INTEGER = 1;
-    private static long ELAPSED_TIME = 1000;
-
-    @Mock private LogWriter mockLogWriter;
-    @Mock private VisibilityLoggerMixin mockVisibilityLogger;
+    @Mock
+    private LogWriter mLogWriter;
 
     private Context mContext;
     private MetricsFeatureProvider mProvider;
 
-    @Captor
-    private ArgumentCaptor<Pair> mPairCaptor;
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
         mProvider = new MetricsFeatureProvider();
         List<LogWriter> writers = new ArrayList<>();
-        writers.add(mockLogWriter);
+        writers.add(mLogWriter);
         ReflectionHelpers.setField(mProvider, "mLoggerWriters", writers);
-
-        when(mockVisibilityLogger.elapsedTimeSinceVisible()).thenReturn(ELAPSED_TIME);
     }
 
     @Test
@@ -77,7 +62,7 @@
         mProvider.logDashboardStartIntent(mContext, null /* intent */,
                 MetricsEvent.SETTINGS_GESTURES);
 
-        verifyNoMoreInteractions(mockLogWriter);
+        verifyNoMoreInteractions(mLogWriter);
     }
 
     @Test
@@ -86,7 +71,7 @@
 
         mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
 
-        verify(mockLogWriter).action(
+        verify(mLogWriter).action(
                 eq(mContext),
                 eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
                 anyString(),
@@ -99,32 +84,10 @@
 
         mProvider.logDashboardStartIntent(mContext, intent, MetricsEvent.SETTINGS_GESTURES);
 
-        verify(mockLogWriter).action(
+        verify(mLogWriter).action(
                 eq(mContext),
                 eq(MetricsEvent.ACTION_SETTINGS_TILE_CLICK),
                 anyString(),
                 eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
     }
-
-    @Test
-    public void action_BooleanLogsElapsedTime() {
-        mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_BOOLEAN);
-        verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_BOOLEAN), mPairCaptor.capture());
-
-        Pair value = mPairCaptor.getValue();
-        assertThat(value.first instanceof Integer).isTrue();
-        assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
-        assertThat(value.second).isEqualTo(ELAPSED_TIME);
-    }
-
-    @Test
-    public void action_IntegerLogsElapsedTime() {
-        mProvider.action(mockVisibilityLogger, CATEGORY, SUBTYPE_INTEGER);
-        verify(mockLogWriter).action(eq(CATEGORY), eq(SUBTYPE_INTEGER), mPairCaptor.capture());
-
-        Pair value = mPairCaptor.getValue();
-        assertThat(value.first instanceof Integer).isTrue();
-        assertThat((int) value.first).isEqualTo(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS);
-        assertThat(value.second).isEqualTo(ELAPSED_TIME);
-    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 8745a33..c996620 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -35,6 +35,13 @@
 
     static void dumpProtoLocked(SettingsProvider.SettingsRegistry settingsRegistry,
             ProtoOutputStream proto) {
+        // Config settings
+        SettingsState configSettings = settingsRegistry.getSettingsLocked(
+                SettingsProvider.SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+        if (configSettings != null) {
+            // TODO(b/113100523): dump configuration settings after they are added
+        }
+
         // Global settings
         SettingsState globalSettings = settingsRegistry.getSettingsLocked(
                 SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
@@ -662,6 +669,9 @@
         dumpSetting(s, p,
                 Settings.Global.ANGLE_ENABLED_APP,
                 GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
+        dumpSetting(s, p,
+                Settings.Global.GPU_DEBUG_LAYER_APP,
+                GlobalSettingsProto.Gpu.DEBUG_LAYER_APP);
         p.end(gpuToken);
 
         final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 18ec9c3..e0c4d72 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -180,6 +180,7 @@
     public static final int SETTINGS_TYPE_SYSTEM = SettingsState.SETTINGS_TYPE_SYSTEM;
     public static final int SETTINGS_TYPE_SECURE = SettingsState.SETTINGS_TYPE_SECURE;
     public static final int SETTINGS_TYPE_SSAID = SettingsState.SETTINGS_TYPE_SSAID;
+    public static final int SETTINGS_TYPE_CONFIG = SettingsState.SETTINGS_TYPE_CONFIG;
 
     private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
             Settings.NameValueTable.VALUE, null);
@@ -189,6 +190,13 @@
     private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
     private static final Set<String> OVERLAY_ALLOWED_SECURE_INSTANT_APP_SETTINGS = new ArraySet<>();
 
+    /**
+     * TODO(b/113100523): Move this to DeviceConfig.java when it is added, and expose it as a System
+     *     API.
+     */
+    private static final Uri CONFIG_CONTENT_URI =
+            Uri.parse("content://" + Settings.AUTHORITY + "/config");
+
     static {
         for (String name : Resources.getSystem().getStringArray(
                 com.android.internal.R.array.config_allowedGlobalInstantAppSettings)) {
@@ -380,6 +388,11 @@
     public Bundle call(String method, String name, Bundle args) {
         final int requestingUserId = getRequestingUserId(args);
         switch (method) {
+            case Settings.CALL_METHOD_GET_CONFIG: {
+                Setting setting = getConfigSetting(name);
+                return packageValueForCallResult(setting, isTrackingGeneration(args));
+            }
+
             case Settings.CALL_METHOD_GET_GLOBAL: {
                 Setting setting = getGlobalSetting(name);
                 return packageValueForCallResult(setting, isTrackingGeneration(args));
@@ -396,6 +409,14 @@
                 return packageValueForCallResult(setting, isTrackingGeneration(args));
             }
 
+            case Settings.CALL_METHOD_PUT_CONFIG: {
+                String value = getSettingValue(args);
+                String tag = getSettingTag(args);
+                final boolean makeDefault = getSettingMakeDefault(args);
+                insertConfigSetting(name, value, tag, makeDefault, requestingUserId, false);
+                break;
+            }
+
             case Settings.CALL_METHOD_PUT_GLOBAL: {
                 String value = getSettingValue(args);
                 String tag = getSettingTag(args);
@@ -418,6 +439,13 @@
                 break;
             }
 
+            case Settings.CALL_METHOD_RESET_CONFIG: {
+                final int mode = getResetModeEnforcingPermission(args);
+                String tag = getSettingTag(args);
+                resetConfigSetting(requestingUserId, mode, tag);
+                break;
+            }
+
             case Settings.CALL_METHOD_RESET_GLOBAL: {
                 final int mode = getResetModeEnforcingPermission(args);
                 String tag = getSettingTag(args);
@@ -725,6 +753,15 @@
     @GuardedBy("mLock")
     private void dumpForUserLocked(int userId, PrintWriter pw) {
         if (userId == UserHandle.USER_SYSTEM) {
+            pw.println("CONFIG SETTINGS (user " + userId + ")");
+            SettingsState configSettings = mSettingsRegistry.getSettingsLocked(
+                    SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+            if (configSettings != null) {
+                dumpSettingsLocked(configSettings, pw);
+                pw.println();
+                configSettings.dumpHistoricalOperations(pw);
+            }
+
             pw.println("GLOBAL SETTINGS (user " + userId + ")");
             SettingsState globalSettings = mSettingsRegistry.getSettingsLocked(
                     SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
@@ -939,6 +976,69 @@
         });
     }
 
+    private Setting getConfigSetting(String name) {
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "getConfigSetting(" + name + ")");
+        }
+
+        // TODO(b/117663715): Ensure the caller can access the setting.
+        // enforceSettingReadable(name, SETTINGS_TYPE_CONFIG, UserHandle.getCallingUserId());
+
+        // Get the value.
+        synchronized (mLock) {
+            return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_CONFIG,
+                    UserHandle.USER_SYSTEM, name);
+        }
+    }
+
+    private boolean insertConfigSetting(String name, String value, String tag,
+            boolean makeDefault, int requestingUserId, boolean forceNotify) {
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "insertConfigSetting(" + name + ", " + value  + ", "
+                    + ", " + tag + ", " + makeDefault + ", " + requestingUserId
+                    + ", " + forceNotify + ")");
+        }
+        return mutateConfigSetting(name, value, tag, makeDefault, requestingUserId,
+                MUTATION_OPERATION_INSERT, forceNotify, 0);
+    }
+
+    private void resetConfigSetting(int requestingUserId, int mode, String tag) {
+        if (DEBUG) {
+            Slog.v(LOG_TAG, "resetConfigSetting(" + requestingUserId + ", "
+                    + mode + ", " + tag + ")");
+        }
+        mutateConfigSetting(null, null, tag, false, requestingUserId,
+                MUTATION_OPERATION_RESET, false, mode);
+    }
+
+    private boolean mutateConfigSetting(String name, String value, String tag,
+            boolean makeDefault, int requestingUserId, int operation, boolean forceNotify,
+            int mode) {
+        // TODO(b/117663715): check the new permission when it's added.
+        // enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
+
+        // Resolve the userId on whose behalf the call is made.
+        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
+
+        // Perform the mutation.
+        synchronized (mLock) {
+            switch (operation) {
+                case MUTATION_OPERATION_INSERT: {
+                    return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG,
+                            UserHandle.USER_SYSTEM, name, value, tag, makeDefault,
+                            getCallingPackage(), forceNotify, null);
+                }
+
+                case MUTATION_OPERATION_RESET: {
+                    mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG,
+                            UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag);
+                } return true;
+            }
+        }
+
+        return false;
+    }
+
     private Cursor getAllGlobalSettings(String[] projection) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getAllGlobalSettings()");
@@ -2132,6 +2232,7 @@
         private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";
         private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";
         private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml";
+        private static final String SETTINGS_FILE_CONFIG = "settings_config.xml";
 
         private static final String SSAID_USER_KEY = "userkey";
 
@@ -2303,6 +2404,13 @@
             // Migrate the setting for this user if needed.
             migrateLegacySettingsForUserIfNeededLocked(userId);
 
+            // Ensure config settings loaded if owner.
+            if (userId == UserHandle.USER_SYSTEM) {
+                final int configKey
+                        = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
+                ensureSettingsStateLocked(configKey);
+            }
+
             // Ensure global settings loaded if owner.
             if (userId == UserHandle.USER_SYSTEM) {
                 final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
@@ -2853,6 +2961,10 @@
             }
         }
 
+        private boolean isConfigSettingsKey(int key) {
+            return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG;
+        }
+
         private boolean isGlobalSettingsKey(int key) {
             return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
         }
@@ -2870,7 +2982,11 @@
         }
 
         private File getSettingsFile(int key) {
-            if (isGlobalSettingsKey(key)) {
+            if (isConfigSettingsKey(key)) {
+                final int userId = getUserIdFromKey(key);
+                return new File(Environment.getUserSystemDirectory(userId),
+                        SETTINGS_FILE_CONFIG);
+            } else if (isGlobalSettingsKey(key)) {
                 final int userId = getUserIdFromKey(key);
                 return new File(Environment.getUserSystemDirectory(userId),
                         SETTINGS_FILE_GLOBAL);
@@ -2892,7 +3008,10 @@
         }
 
         private Uri getNotificationUriFor(int key, String name) {
-            if (isGlobalSettingsKey(key)) {
+            if (isConfigSettingsKey(key)) {
+                return (name != null) ? Uri.withAppendedPath(CONFIG_CONTENT_URI, name)
+                        : CONFIG_CONTENT_URI;
+            } else if (isGlobalSettingsKey(key)) {
                 return (name != null) ? Uri.withAppendedPath(Settings.Global.CONTENT_URI, name)
                         : Settings.Global.CONTENT_URI;
             } else if (isSecureSettingsKey(key)) {
@@ -2908,6 +3027,7 @@
 
         private int getMaxBytesPerPackageForType(int type) {
             switch (type) {
+                case SETTINGS_TYPE_CONFIG:
                 case SETTINGS_TYPE_GLOBAL:
                 case SETTINGS_TYPE_SECURE:
                 case SETTINGS_TYPE_SSAID: {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 389d627..ae2ca3f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -34,7 +34,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.providers.settings.GlobalSettingsProto;
 import android.providers.settings.SettingsOperationProto;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -67,7 +66,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * This class contains the state for one type of settings. It is responsible
@@ -205,6 +203,7 @@
     public static final int SETTINGS_TYPE_SYSTEM = 1;
     public static final int SETTINGS_TYPE_SECURE = 2;
     public static final int SETTINGS_TYPE_SSAID = 3;
+    public static final int SETTINGS_TYPE_CONFIG = 4;
 
     public static final int SETTINGS_TYPE_MASK = 0xF0000000;
     public static final int SETTINGS_TYPE_SHIFT = 28;
@@ -223,6 +222,9 @@
 
     public static String settingTypeToString(int type) {
         switch (type) {
+            case SETTINGS_TYPE_CONFIG: {
+                return "SETTINGS_CONFIG";
+            }
             case SETTINGS_TYPE_GLOBAL: {
                 return "SETTINGS_GLOBAL";
             }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index 95569dc..572a924 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -645,7 +645,7 @@
                         return;
                     }
                     final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
-                    if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
+                    if (elapsedTimeMillis >= WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) {
                         fail("Could not change setting for "
                                 + WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms");
                     }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index dcc6cba..822c39b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -56,6 +56,7 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
     <!-- Development tool permissions granted to the shell. -->
     <uses-permission android:name="android.permission.SET_DEBUG_APP" />
     <uses-permission android:name="android.permission.SET_PROCESS_LIMIT" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index b58ea00..f492208 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.plugins;
 
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Intent;
 
@@ -36,7 +37,17 @@
     void postStartActivityDismissingKeyguard(PendingIntent intent);
     void postQSRunnableDismissingKeyguard(Runnable runnable);
 
+    void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
+            boolean afterKeyguardGone);
+
     interface Callback {
         void onActivityStarted(int resultCode);
     }
+
+    interface OnDismissAction {
+        /**
+         * @return {@code true} if the dismiss should be deferred
+         */
+        boolean onDismiss();
+    }
 }
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
new file mode 100644
index 0000000..21c9051
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector android:height="36sp" android:viewportHeight="36"
+        android:viewportWidth="36" android:width="36sp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="?android:attr/colorAccent" android:pathData="M18,18m-18,0a18,18 0,1 1,36 0a18,18 0,1 1,-36 0"/>
+    <path android:fillColor="?android:attr/textColorPrimaryInverse" android:pathData="M17.59,13.41L21.17,17H7v2h14.17l-3.59,3.59L19,24l6,-6l-6,-6L17.59,13.41zM26,12v12h2V12H26z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml b/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml
new file mode 100644
index 0000000..51c442a
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ripple_drawable_pin.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?android:attr/colorControlHighlight"
+        android:radius="40dp"/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 41dd0b3..9c41fca 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -57,24 +57,6 @@
                     android:textColor="?attr/wallpaperTextColor"
                     android:contentDescription="@string/keyguard_accessibility_pin_area"
                     />
-            <ImageButton
-                    android:id="@+id/delete_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="match_parent"
-                    android:gravity="center_vertical"
-                    android:src="@drawable/ic_backspace_black_24dp"
-                    android:clickable="true"
-                    android:paddingTop="8dip"
-                    android:paddingBottom="8dip"
-                    android:paddingRight="0dp"
-                    android:paddingLeft="0dp"
-                    android:background="@drawable/ripple_drawable"
-                    android:contentDescription="@string/keyboardview_keycode_delete"
-                    android:layout_alignEnd="@+id/pinEntry"
-                    android:layout_alignParentRight="true"
-                    android:tint="@color/pin_delete_color"
-                    android:tintMode="src_in"
-                    />
             <View
                     android:id="@+id/divider"
                     android:layout_width="match_parent"
@@ -186,10 +168,14 @@
                 android:layout_weight="1"
                 android:orientation="horizontal"
                 >
-            <Space
+            <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/delete_button"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
+                    android:background="@drawable/ripple_drawable_pin"
+                    android:contentDescription="@string/keyboardview_keycode_delete"
+                    style="@style/Keyguard.ImageButton.NumPadDelete"
                     />
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key0"
@@ -204,10 +190,8 @@
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    android:paddingBottom="11sp"
-                    android:src="@drawable/ic_done_black_24dp"
                     style="@style/Keyguard.ImageButton.NumPadEnter"
-                    android:background="@drawable/ripple_drawable"
+                    android:background="@drawable/ripple_drawable_pin"
                     android:contentDescription="@string/keyboardview_keycode_enter"
                     />
         </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index a795442..7d8a1f5b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -42,18 +42,11 @@
                          android:id="@+id/clock_view"
                          android:layout_width="match_parent"
                          android:layout_height="wrap_content" />
-                <View
-                    android:id="@+id/clock_separator"
-                    android:layout_width="@dimen/widget_separator_width"
-                    android:layout_height="@dimen/widget_separator_thickness"
-                    android:layout_below="@id/clock_view"
-                    android:background="#f00"
-                    android:layout_centerHorizontal="true" />
                 <include layout="@layout/keyguard_status_area"
                          android:id="@+id/keyguard_status_area"
                          android:layout_width="match_parent"
                          android:layout_height="wrap_content"
-                         android:layout_below="@id/clock_separator" />
+                         android:layout_below="@id/clock_view" />
             </RelativeLayout>
             <ImageView
                 android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index 33f7e75..bfb5bf9 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -71,24 +71,6 @@
                     android:textColor="?attr/wallpaperTextColor"
                     android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
                     />
-            <ImageButton
-                    android:id="@+id/delete_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="match_parent"
-                    android:gravity="center_vertical"
-                    android:src="@drawable/ic_backspace_black_24dp"
-                    android:clickable="true"
-                    android:paddingTop="8dip"
-                    android:paddingBottom="8dip"
-                    android:paddingRight="0dp"
-                    android:paddingLeft="0dp"
-                    android:background="@drawable/ripple_drawable"
-                    android:contentDescription="@string/keyboardview_keycode_delete"
-                    android:layout_alignEnd="@+id/pinEntry"
-                    android:layout_alignParentRight="true"
-                    android:tint="@color/pin_delete_color"
-                    android:tintMode="src_in"
-                    />
             <View
                     android:id="@+id/divider"
                     android:layout_width="match_parent"
@@ -196,10 +178,14 @@
                 android:layout_weight="1"
                 android:orientation="horizontal"
                 >
-            <Space
+            <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/delete_button"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
+                    android:background="@drawable/ripple_drawable_pin"
+                    android:contentDescription="@string/keyboardview_keycode_delete"
+                    style="@style/Keyguard.ImageButton.NumPadDelete"
                     />
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key0"
@@ -209,15 +195,13 @@
                     androidprv:textView="@+id/simPinEntry"
                     androidprv:digit="0"
                     />
-            <ImageButton
+            <com.android.keyguard.AlphaOptimizedImageButton
                     android:id="@+id/key_enter"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    android:paddingBottom="11sp"
-                    android:src="@drawable/ic_done_black_24dp"
                     style="@style/Keyguard.ImageButton.NumPadEnter"
-                    android:background="@drawable/ripple_drawable"
+                    android:background="@drawable/ripple_drawable_pin"
                     android:contentDescription="@string/keyboardview_keycode_enter"
                     />
         </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 4b385fc..9f3ae3a 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -72,24 +72,6 @@
                     android:textColor="?attr/wallpaperTextColor"
                     android:contentDescription="@string/keyguard_accessibility_sim_puk_area"
                     />
-            <ImageButton
-                    android:id="@+id/delete_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="match_parent"
-                    android:gravity="center_vertical"
-                    android:src="@drawable/ic_backspace_black_24dp"
-                    android:clickable="true"
-                    android:paddingTop="8dip"
-                    android:paddingBottom="8dip"
-                    android:paddingRight="0dp"
-                    android:paddingLeft="0dp"
-                    android:background="@drawable/ripple_drawable"
-                    android:contentDescription="@string/keyboardview_keycode_delete"
-                    android:layout_alignEnd="@+id/pinEntry"
-                    android:layout_alignParentRight="true"
-                    android:tint="@color/pin_delete_color"
-                    android:tintMode="src_in"
-                    />
             <View
                     android:id="@+id/divider"
                     android:layout_width="match_parent"
@@ -197,10 +179,14 @@
                 android:layout_weight="1"
                 android:orientation="horizontal"
                 >
-            <Space
+            <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/delete_button"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
+                    android:background="@drawable/ripple_drawable_pin"
+                    android:contentDescription="@string/keyboardview_keycode_delete"
+                    style="@style/Keyguard.ImageButton.NumPadDelete"
                     />
             <com.android.keyguard.NumPadKey
                     android:id="@+id/key0"
@@ -210,15 +196,13 @@
                     androidprv:textView="@+id/pukEntry"
                     androidprv:digit="0"
                     />
-            <ImageButton
+            <com.android.keyguard.AlphaOptimizedImageButton
                     android:id="@+id/key_enter"
                     android:layout_width="0px"
                     android:layout_height="match_parent"
                     android:layout_weight="1"
-                    android:paddingBottom="11sp"
-                    android:src="@drawable/ic_done_black_24dp"
                     style="@style/Keyguard.ImageButton.NumPadEnter"
-                    android:background="@drawable/ripple_drawable"
+                    android:background="@drawable/ripple_drawable_pin"
                     android:contentDescription="@string/keyboardview_keycode_enter"
                     />
         </LinearLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index dfa4bf9..00f8f86 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -43,6 +43,7 @@
               android:id="@+id/row"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
+              android:layout_marginTop="@dimen/subtitle_clock_padding"
               android:orientation="horizontal"
               android:gravity="center"
     />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 4ae2d41..32a7147 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -59,19 +59,11 @@
                  android:id="@+id/clock_view"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content" />
-            <View
-                android:id="@+id/clock_separator"
-                android:layout_width="@dimen/widget_separator_width"
-                android:layout_height="@dimen/widget_separator_thickness"
-                android:layout_below="@id/clock_view"
-                android:background="#f00"
-                android:layout_centerHorizontal="true" />
-
             <include layout="@layout/keyguard_status_area"
                 android:id="@+id/keyguard_status_area"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_below="@id/clock_separator" />
+                android:layout_below="@id/clock_view" />
         </RelativeLayout>
 
         <TextView
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index 9984061..1106807 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -85,9 +85,9 @@
     <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-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 <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_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> s."</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> s."</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> s."</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>
     <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="3921998703529189931">"Pokušali ste <xliff:g id="NUMBER_0">%1$d</xliff:g> puta neispravno otključati telefon. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, telefon će se vratiti na fabričke postavke i svi podaci će se izbrisati."</string>
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="4694232971224663735">"Pokušali ste <xliff:g id="NUMBER">%d</xliff:g> puta neispravno otključati tablet. Tablet će se sada vratiti na fabričke postavke i svi podaci će se izbrisati."</string>
@@ -100,8 +100,8 @@
     <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="2162434417489128282">"Pokušali ste <xliff:g id="NUMBER_0">%1$d</xliff:g> puta neispravno otključati telefon. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, poslovni profil će se ukloniti i svi podaci s profila će se izbrisati."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="8966727588974691544">"Pokušali ste <xliff:g id="NUMBER">%d</xliff:g> puta neispravno otključati tablet. Poslovni profil će se ukloniti i svi podaci s profila će se izbrisati."</string>
     <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_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> s."</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> s."</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 za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index c2759da..d412252 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -37,7 +37,7 @@
     <string name="keyguard_plugged_in" msgid="3161102098900158923">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="3684592786276709342">"<xliff:g id="PERCENTAGE">%s</xliff:g> • वेगाने चार्ज होत आहे"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="509533586841478405">"<xliff:g id="PERCENTAGE">%s</xliff:g> • सावकाश चार्ज होत आहे"</string>
-    <string name="keyguard_low_battery" msgid="9218432555787624490">"आपला चार्जर कनेक्ट करा."</string>
+    <string name="keyguard_low_battery" msgid="9218432555787624490">"तुमचा चार्जर कनेक्ट करा."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"अनलॉक करण्यासाठी मेनू दाबा."</string>
     <string name="keyguard_network_locked_message" msgid="6743537524631420759">"नेटवर्क लॉक केले"</string>
     <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"सिम कार्ड नाही"</string>
@@ -84,9 +84,9 @@
     <string name="kg_invalid_puk" msgid="5399287873762592502">"योग्य PUK कोड पुन्हा एंटर करा. पुनःपुन्हा प्रयत्न करणे सिम कायमचे अक्षम करेल."</string>
     <string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"पिन कोड जुळत नाहीत"</string>
     <string name="kg_login_too_many_attempts" msgid="6604574268387867255">"खूप जास्त पॅटर्न प्रयत्न"</string>
-    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"तुम्ही आपला PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7724148763268377734">"तुम्ही आपला पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4820967667848302092">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"तुम्ही तुमचा PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7724148763268377734">"तुम्ही तुमचा पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4820967667848302092">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1629351522209932316">"तुम्ही टॅबलेट अनलॉक करण्याचा <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने प्रयत्न केला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, हे टॅबलेट रीसेट केला जाईल, जे त्याचा सर्व डेटा हटवेल."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="3921998703529189931">"तुम्ही फोन अनलॉक करण्याचा <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने प्रयत्न केला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, हा फोन रीसेट केला जाईल, जे त्याचा सर्व डेटा हटवेल."</string>
     <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="4694232971224663735">"तुम्ही टॅबलेट अनलॉक करण्याचा <xliff:g id="NUMBER">%d</xliff:g> वेळा चुकीच्या पद्धतीने प्रयत्न केला आहे. हे टॅबलेट रीसेट केले जाईल, जे त्याचा सर्व डेटा हटवेल."</string>
@@ -99,8 +99,8 @@
     <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="2162434417489128282">"तुम्ही फोन अनलॉक करण्याचा <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने प्रयत्न केला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, ही कार्य प्रोफाइल काढली जाईल, जे सर्व प्रोफाइल डेटा हटवेल."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="8966727588974691544">"तुम्ही टॅबलेट अनलॉक करण्याचा <xliff:g id="NUMBER">%d</xliff:g> वेळा चुकीच्या पद्धतीने प्रयत्न केला आहे. कार्य प्रोफाइल काढली जाईल, जे सर्व प्रोफाइल डेटा हटवेल."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"तुम्ही फोन अनलॉक करण्याचा <xliff:g id="NUMBER">%d</xliff:g> वेळा चुकीच्या पद्धतीने प्रयत्न केला आहे. कार्य प्रोफाइल काढली जाईल, जे सर्व प्रोफाइल डेटा हटवेल."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून आपला टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"तुम्ही आपला अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून आपला फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा टॅब्लेट अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यपणे काढला आहे. आणखी <xliff:g id="NUMBER_1">%2$d</xliff:g> अयशस्वी प्रयत्नांनंतर, तुम्हाला ईमेल खाते वापरून तुमचा फोन अनलॉक करण्यास सांगितले जाईल.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"सिम पिन कोड चुकीचा आहे तुम्ही आता तुमचे डिव्हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधावा."</string>
     <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
       <item quantity="one">चुकीचा सिम पिन कोड, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 787f91e..555f443 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -43,7 +43,7 @@
 
     <!-- Slice header -->
     <dimen name="widget_title_font_size">24dp</dimen>
-    <dimen name="widget_title_bottom_margin">7dp</dimen>
+    <dimen name="widget_title_bottom_margin">14dp</dimen>
     <dimen name="bottom_text_spacing_digital">0dp</dimen>
     <!-- Slice subtitle -->
     <dimen name="widget_label_font_size">16dp</dimen>
@@ -52,19 +52,17 @@
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
     <!-- Clock with header -->
+    <dimen name="widget_small_clock_padding">-25dp</dimen>
     <dimen name="widget_small_font_size">24dp</dimen>
     <dimen name="widget_small_font_stroke">0.6dp</dimen>
-    <!-- Dash between clock and header -->
-    <dimen name="widget_separator_width">12dp</dimen>
-    <dimen name="widget_separator_thickness">1dp</dimen>
-    <dimen name="widget_vertical_padding">26dp</dimen>
-    <dimen name="widget_icon_bottom_padding">14dp</dimen>
+    <dimen name="widget_vertical_padding">32dp</dimen>
     <!-- Subtitle paddings -->
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
     <dimen name="widget_icon_padding">8dp</dimen>
-    <!-- Space between notification shelf and dash above it -->
-    <dimen name="widget_bottom_separator_padding">28dp</dimen>
+    <dimen name="subtitle_clock_padding">15dp</dimen>
+    <!-- Notification shelf padding when dark -->
+    <dimen name="widget_bottom_separator_padding">-6dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
     <dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index b90b4dd..0462347 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -37,8 +37,16 @@
         <item name="android:textColor">?attr/wallpaperTextColor</item>
         <item name="android:paddingBottom">-16dp</item>
     </style>
+    <style name="Keyguard.ImageButton.NumPadDelete" parent="@android:style/Widget.ImageButton">
+        <item name="android:src">@drawable/ic_backspace_black_24dp</item>
+        <item name="android:paddingBottom">11sp</item>
+        <item name="android:tint">@color/pin_delete_color</item>
+        <item name="android:tintMode">src_in</item>
+        <item name="android:src">@drawable/ic_backspace_black_24dp</item>
+    </style>
     <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.ImageButton">
-        <item name="android:tint">@color/background_protected</item>
+        <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
+        <item name="android:paddingBottom">11sp</item>
     </style>
     <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
         <item name="android:textSize">12sp</item>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 3077433..395de19 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Diensverskaffernetwerk verander tans"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Maak batterybesonderhede oop"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Battery <xliff:g id="NUMBER">%d</xliff:g> persent."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Battery laai tans, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> persent."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Battery laai tans, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Stelselinstellings"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Kennisgewings"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Sien alle kennisgewings"</string>
@@ -319,6 +319,8 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Onbenoemde toestel"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gereed om uit te saai"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Geen toestelle beskikbaar nie"</string>
+    <!-- no translation found for quick_settings_cast_no_wifi (2696477881905521882) -->
+    <skip />
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Keer kleure om"</string>
@@ -782,6 +784,8 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Aangemeld as <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Geen internet nie"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Maak besonderhede oop."</string>
+    <!-- no translation found for accessibility_quick_settings_not_available (4190068184294019846) -->
+    <skip />
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Maak <xliff:g id="ID_1">%s</xliff:g>-instellings oop."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Wysig volgorde van instellings."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Bladsy <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c0c0952..fcad97b 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"የአገልግሎት አቅራቢ አውታረ መረብን በመቀየር ላይ"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"የባትሪ ዝርዝሮችን ክፈት"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"የባትሪ <xliff:g id="NUMBER">%d</xliff:g> መቶኛ።"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ባትሪ ኃይል በመሙላት ላይ፣ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> በመቶ።"</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ባትሪ ኃይል በመሙላት ላይ፣ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"የስርዓት ቅንብሮች"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"ማሳወቂያዎች"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"ሁሉንም ማሳወቂያዎች ይመልከቱ"</string>
@@ -319,6 +319,8 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ያልተሰየመ መሳሪያ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ለመውሰድ ዝግጁ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ምንም መሣሪያዎች አይገኙም"</string>
+    <!-- no translation found for quick_settings_cast_no_wifi (2696477881905521882) -->
+    <skip />
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ብሩህነት"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ራስ-ሰር"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ቀለማትን ግልብጥ"</string>
@@ -782,6 +784,8 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"እንደ <xliff:g id="ID_1">%s</xliff:g> ሆነው ገብተዋል"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ምንም በይነመረብ የለም"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ዝርዝሮችን ክፈት።"</string>
+    <!-- no translation found for accessibility_quick_settings_not_available (4190068184294019846) -->
+    <skip />
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"የ<xliff:g id="ID_1">%s</xliff:g> ቅንብሮችን ክፈት።"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"የቅንብሮድ ቅደም-ተከተል አርትዕ።"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ገጽ <xliff:g id="ID_1">%1$d</xliff:g> ከ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index f7d5184..1b55d39 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -178,7 +178,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"فتح تفاصيل البطارية"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"مستوى البطارية <xliff:g id="NUMBER">%d</xliff:g> في المائة."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"جارٍ شحن البطارية، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> بالمائة."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"جارٍ شحن البطارية، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"إعدادات النظام."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"الإشعارات."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"الاطّلاع على جميع الإشعارات"</string>
@@ -327,6 +327,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"جهاز لا يحمل اسمًا"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"جاهز للإرسال"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"لا يتوفر أي جهاز"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"‏لم يتم الاتصال بشبكة Wi-Fi."</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"السطوع"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"تلقائي"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"قلب الألوان"</string>
@@ -806,6 +807,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"تم تسجيل الدخول باعتبارك <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"لا يتوفر اتصال إنترنت."</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"فتح التفاصيل."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"غير متاحة بسبب <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"فتح إعدادات <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"تعديل ترتيب الإعدادات."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"الصفحة <xliff:g id="ID_1">%1$d</xliff:g> من <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -846,7 +848,7 @@
     <string name="notification_channel_battery" msgid="5786118169182888462">"البطارية"</string>
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"لقطات الشاشة"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"رسائل عامة"</string>
-    <string name="notification_channel_storage" msgid="3077205683020695313">"سعة التخزين"</string>
+    <string name="notification_channel_storage" msgid="3077205683020695313">"مساحة التخزين"</string>
     <string name="notification_channel_hints" msgid="7323870212489152689">"تلميحات"</string>
     <string name="instant_apps" msgid="6647570248119804907">"التطبيقات الفورية"</string>
     <string name="instant_apps_title" msgid="8738419517367449783">"التطبيق <xliff:g id="APP">%1$s</xliff:g> قيد التشغيل"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 895faf8..65c5fb5 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"নাম নথকা ডিভাইচ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"কাষ্টৰ বাবে সাজু"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"কোনো ডিভাইচ নাই"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ৱাই-ফাইৰ সৈতে সংযোগ হৈ থকা নাই"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"উজ্জ্বলতা"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"স্বয়ং"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ৰং ওলোটা কৰক"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"আপোনাৰ স্ক্ৰীণত প্ৰদৰ্শন হোৱা সকলো <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> কেপশ্বাৰ কৰা আৰম্ভ কৰিব।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"পুনৰাই নেদেখুৱাব"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"সকলো মচক"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"পৰিচালনা"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"অসুবিধা নিদিব-ই জাননী পজ কৰিছে"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"এতিয়াই আৰম্ভ কৰক"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"কোনো জাননী নাই"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> হিচাপে ছাইন ইন হ\'ল"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"বিৱৰণসমূহ খোলক।"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>ৰ বাবে উপলব্ধ নহয়"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিংসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ছেটিংসমূহৰ ক্ৰম সম্পাদনা কৰক।"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 461a328..07476aa 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Operator şəbəkəsinin dəyişilməsi"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Batareya detallarını açın"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batareya <xliff:g id="NUMBER">%d</xliff:g> faizdir."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batareya doldurulur, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> faiz."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batareya doldurulur, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%% faiz."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistem parametrləri"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Bildirişlər."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Bütün bildirişlərə baxın"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Adsız cihaz"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Yayıma hazırdır"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Heç bir cihaz əlçatan deyil"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi qoşulu deyil"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Parlaqlıq"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AVTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Rəngləri çevirin"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> kimi daxil olunub"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"İnternet yoxdur"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Detalları açın."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> səbəbi ilə əlçatan deyil"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ayarlarını açın."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Ayarların sıralanmasını redaktə edin."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> səhifədən <xliff:g id="ID_1">%1$d</xliff:g> səhifə"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index ade59ab..db3834b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -175,7 +175,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Promena mreže mobilnog operatera"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Otvori detalje o bateriji"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterija je na <xliff:g id="NUMBER">%d</xliff:g> posto."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Baterija se puni, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procenata."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Baterija se puni, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistemska podešavanja."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Obaveštenja."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Pogledajte sva obaveštenja"</string>
@@ -321,6 +321,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovani uređaj"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Spremno za prebacivanje"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nije dostupan nijedan uređaj"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi nije povezan"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Osvetljenost"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATSKA"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Obrni boje"</string>
@@ -788,6 +789,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prijavljeni ste kao <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nema interneta"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otvori detalje."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nije dostupno iz sledećeg razloga: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvori podešavanja za <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Izmeni redosled podešavanja."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. strana od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index bea5c3f..7b19b36 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -178,7 +178,7 @@
     <!-- String.format failed for translation -->
     <!-- no translation found for accessibility_battery_level (7451474187113371965) -->
     <skip />
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Зарадка акумулятара, працэнтаў: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Зарадка акумулятара: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Сістэмныя налады."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Апавяшчэнні."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Паказаць усе апавяшчэнні"</string>
@@ -325,6 +325,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Прылада без назвы"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Гатова для трансляцыі"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Няма даступных прылад"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi не падключаны"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркасць"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АЎТА"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Інвертаваць колеры"</string>
@@ -796,6 +797,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Вы ўвайшлі як <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Не падключана да інтэрнэту"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Паказаць падрабязную інфармацыю."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Прычына недаступнасці: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Адкрыць налады <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Змяніць парадак налад."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Старонка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c321593..74b1c44 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Промяна на мрежата на оператора"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Отваряне на подробностите за батерията"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> процента батерия."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батерията се зарежда – <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> процента."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батерията се зарежда – <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Системни настройки."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Известия."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Вижте всички известия"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Устройство без име"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Готово за предаване"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Няма налични устройства"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"не е установена връзка с Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркост"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТ."</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Инвертиране на цветовете"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Влезли сте като <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Няма връзка с интернет"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Отвaряне на страницата с подробности."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Няма достъп, защото <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Отваряне на настройките за <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Редактиране на подредбата на настройките."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Страница <xliff:g id="ID_1">%1$d</xliff:g> от <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 138f2b0..5223a99 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"নামবিহীন ডিভাইস"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"কাস্ট করার জন্য প্রস্তুত"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"কোনো ডিভাইস উপলব্ধ নয়"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ওয়াই-ফাই কানেক্ট করা নেই"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"উজ্জ্বলতা"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"স্বয়ং"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"বিপরীত রঙ"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> আপনার স্ক্রীনে দেখানো সব কিছু ক্যাপচার করা শুরু করবে।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"আর দেখাবেন না"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"সবকিছু সাফ করুন"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"পরিচালনা করুন"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'বিরক্ত করবেন না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"এখন শুরু করুন"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"কোনো বিজ্ঞপ্তি নেই"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> হিসেবে প্রবেশ করে রয়েছেন"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ইন্টারনেট কানেকশন নেই"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"বিশদ বিবরণ খুলুন৷"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>-এর জন্য পাওয়া যাবে না"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> সেটিংস খুলুন৷"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ক্রম বা সেটিংস সম্পাদনা করুন৷"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>টির মধ্যে <xliff:g id="ID_1">%1$d</xliff:g> নং পৃষ্ঠা"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 88115a5..ca09219 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -175,7 +175,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Promjena mreže mobilnog operatera"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Otvori detalje o potrošnji baterije"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterija na <xliff:g id="NUMBER">%d</xliff:g> posto."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Punjenje baterije, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procenata."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Punjenje baterije, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Postavke sistema."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Obavještenja."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Vidite sva obavještenja"</string>
@@ -321,6 +321,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovani uređaj"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Spreman za emitiranje"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nema dostupnih uređaja"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi mreža nije povezana"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Osvjetljenje"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverzija boja"</string>
@@ -790,6 +791,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prijavljeni ste kao <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nema internetske veze"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otvori detalje."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nije dostupno jer <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvori postavke za: <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Urediti raspored postavki."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 27f043a..7b4fdad 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -63,7 +63,7 @@
     <string name="always_use_device" msgid="4015357883336738417">"Obre sempre <xliff:g id="APPLICATION">%1$s</xliff:g> quan es connecti <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="3257892669444535154">"Obre sempre <xliff:g id="APPLICATION">%1$s</xliff:g> quan es connecti <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
     <string name="usb_debugging_title" msgid="4513918393387141949">"Vols permetre la depuració per USB?"</string>
-    <string name="usb_debugging_message" msgid="2220143855912376496">"L\'empremta digital de la clau de RSA de l\'equip és:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+    <string name="usb_debugging_message" msgid="2220143855912376496">"L\'empremta digital de la clau RSA de l\'equip és:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Dona sempre permís des d\'aquest equip"</string>
     <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No es permet la depuració per USB"</string>
     <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'usuari que té iniciada la sessió al dispositiu en aquest moment no pot activar la depuració per USB. Per utilitzar aquesta funció, cal canviar a l\'usuari principal."</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositiu sense nom"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"A punt per emetre"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No hi ha cap dispositiu disponible."</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"La Wi‑Fi no està connectada"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillantor"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÀTICA"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverteix els colors"</string>
@@ -659,7 +660,7 @@
     <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Esquerra"</string>
     <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Dreta"</string>
     <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centre"</string>
-    <string name="keyboard_key_tab" msgid="3871485650463164476">"Pestanya"</string>
+    <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulador"</string>
     <string name="keyboard_key_space" msgid="2499861316311153293">"Espai"</string>
     <string name="keyboard_key_enter" msgid="5739632123216118137">"Retorn"</string>
     <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retrocés"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"S\'ha iniciat la sessió com a <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Sense connexió a Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Obre la informació detallada."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"No està disponible pel motiu següent: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Obre la configuració per a <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edita l\'ordre de la configuració."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pàgina <xliff:g id="ID_1">%1$d</xliff:g> (<xliff:g id="ID_2">%2$d</xliff:g> en total)"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7825ade..f8f79ce 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -176,9 +176,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Probíhá změna sítě operátora"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Otevřít podrobnosti o baterii"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Stav baterie: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Baterie se nabíjí. Nabito: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Systémová nastavení."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Oznámení."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Zobrazit všechna oznámení"</string>
@@ -325,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nepojmenované zařízení"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Připraveno k vysílání"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nejsou dostupná žádná zařízení"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Není připojena Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jas"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Převrátit barvy"</string>
@@ -796,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Jste přihlášeni jako <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nejste připojeni k internetu"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otevřít podrobnosti."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nedostupné, protože <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otevřít nastavení aplikace <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Upravit pořadí nastavení."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stránka <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 7ddcb5a..7894361 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Skift af mobilnetværk"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Åbn oplysninger om batteri"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batteri <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batteriet oplades. <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procent."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batteriet oplades. <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Systemindstillinger."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Underretninger."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Se alle underretninger"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Enhed uden navn"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klar til at caste"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Der er ingen tilgængelige enheder"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Manglende Wi-Fi-forbindelse"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Byt om på farver"</string>
@@ -362,15 +363,15 @@
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> kunne ikke startes."</string>
     <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> er deaktiveret i sikker tilstand."</string>
     <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ryd alle"</string>
-    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Træk hertil for at bruge delt skærm"</string>
+    <string name="recents_drag_hint_message" msgid="2649739267073203985">"Træk hertil for at bruge opdelt skærm"</string>
     <string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Stryg opad for at skifte apps"</string>
     <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Træk til højre for hurtigt at skifte app"</string>
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Opdel vandret"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Opdel lodret"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Opdel brugerdefineret"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Delt skærm øverst"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Delt skærm til venstre"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Delt skærm til højre"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Opdelt skærm øverst"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Opdelt skærm til venstre"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Opdelt skærm til højre"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Slå Oversigt til/fra"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opladet"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Oplader"</string>
@@ -752,7 +753,7 @@
     <item msgid="3327323682209964956">"Vis ikke dette ikon"</item>
   </string-array>
     <string name="other" msgid="4060683095962566764">"Andet"</string>
-    <string name="accessibility_divider" msgid="5903423481953635044">"Adskiller til delt skærm"</string>
+    <string name="accessibility_divider" msgid="5903423481953635044">"Adskiller til opdelt skærm"</string>
     <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"Vis venstre del i fuld skærm"</string>
     <string name="accessibility_action_divider_left_70" msgid="3612060638991687254">"Venstre 70 %"</string>
     <string name="accessibility_action_divider_left_50" msgid="1248083470322193075">"Venstre 50 %"</string>
@@ -771,8 +772,8 @@
     <string name="accessibility_qs_edit_tile_move" msgid="3108103090006972938">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g> til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redigeringsværktøj for Hurtige indstillinger."</string>
     <string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g>-underretning: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="dock_forced_resizable" msgid="5914261505436217520">"Appen fungerer muligvis ikke i delt skærm."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"Appen understøtter ikke delt skærm."</string>
+    <string name="dock_forced_resizable" msgid="5914261505436217520">"Appen fungerer muligvis ikke i opdelt skærm."</string>
+    <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"Appen understøtter ikke opdelt skærm."</string>
     <string name="forced_resizable_secondary_display" msgid="4230857851756391925">"Appen fungerer muligvis ikke på sekundære skærme."</string>
     <string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"Appen kan ikke åbnes på sekundære skærme."</string>
     <string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"Åbn Indstillinger."</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Logget ind som <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Intet internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Åbn oplysninger."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Ikke tilgængelig på grund af <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åbn <xliff:g id="ID_1">%s</xliff:g>-indstillinger."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Rediger rækkefølgen af indstillinger."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Side <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index cd20971..47aaaed 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -323,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unbenanntes Gerät"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Startklar"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Keine Geräte verfügbar"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"WLAN nicht verbunden"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helligkeit"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Farben umkehren"</string>
@@ -786,6 +787,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Angemeldet als <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Kein Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Details öffnen."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Aus diesem Grund nicht verfügbar: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Einstellungen für <xliff:g id="ID_1">%s</xliff:g> öffnen."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Reihenfolge der Einstellungen bearbeiten."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Seite <xliff:g id="ID_1">%1$d</xliff:g> von <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -858,10 +860,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Zulassen"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Ablehnen"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Tippen zum Planen des Energiesparmodus"</string>
-    <string name="auto_saver_text" msgid="6324376061044218113">"Automatisch aktivieren bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string>
+    <string name="auto_saver_text" msgid="6324376061044218113">"Automatisch aktivieren bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %%"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Nein danke"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Geplanter Energiesparmodus aktiviert"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g>%% automatisch aktiviert."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Einstellungen"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 0a35aec..ea16ba4 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Αλλαγή δικτύου εταιρείας κινητής τηλεφωνίας"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Άνοιγμα λεπτομερειών μπαταρίας"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Μπαταρία <xliff:g id="NUMBER">%d</xliff:g> τοις εκατό."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Φόρτιση μπαταρίας, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> τοις εκατό."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Φόρτιση μπαταρίας, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%% τοις εκατό."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Ρυθμίσεις συστήματος."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Ειδοποιήσεις."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Δείτε όλες τις ειδοποιήσεις"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Ανώνυμη συσκευή"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Έτοιμο για μετάδοση"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Δεν υπάρχουν διαθέσιμες συσκευές"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Το Wi-Fi δεν είναι συνδεδεμένο"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Φωτεινότητα"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ΑΥΤΟΜΑΤΗ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Αντιστροφή χρωμάτων"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Σύνδεση ως <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Άνοιγμα λεπτομερειών."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Μη διαθέσιμα λόγω <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Άνοιγμα ρυθμίσεων <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Επεξεργασία σειράς ρυθμίσεων."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Σελίδα <xliff:g id="ID_1">%1$d</xliff:g> από <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 7fe3d9d..b7da383 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No devices available"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
@@ -443,7 +444,7 @@
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string>
     <string name="manage_notifications_text" msgid="2386728145475108753">"Manage"</string>
-    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string>
+    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do Not Disturb"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Signed in as <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"No Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Open details."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Unvailable due to <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 379e112..19e9b64 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No devices available"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
@@ -443,7 +444,7 @@
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string>
     <string name="manage_notifications_text" msgid="2386728145475108753">"Manage"</string>
-    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string>
+    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do Not Disturb"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Signed in as <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"No Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Open details."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Unvailable due to <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 7fe3d9d..b7da383 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No devices available"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
@@ -443,7 +444,7 @@
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string>
     <string name="manage_notifications_text" msgid="2386728145475108753">"Manage"</string>
-    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string>
+    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do Not Disturb"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Signed in as <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"No Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Open details."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Unvailable due to <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 7fe3d9d..b7da383 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No devices available"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi not connected"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
@@ -443,7 +444,7 @@
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string>
     <string name="manage_notifications_text" msgid="2386728145475108753">"Manage"</string>
-    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do not disturb"</string>
+    <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications paused by Do Not Disturb"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"Profile may be monitored"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Signed in as <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"No Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Open details."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Unvailable due to <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Open <xliff:g id="ID_1">%s</xliff:g> settings."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit order of settings."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> of <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 5be13ea..97ddf61 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎Unnamed device‎‏‎‎‏‎"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎Ready to cast‎‏‎‎‏‎"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎No devices available‎‏‎‎‏‎"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎Wi‑Fi not connected‎‏‎‎‏‎"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‎Brightness‎‏‎‎‏‎"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎AUTO‎‏‎‎‏‎"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎Invert colors‎‏‎‎‏‎"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎Signed in as ‎‏‎‎‏‏‎<xliff:g id="ID_1">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‏‎No internet‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎Open details.‎‏‎‎‏‎"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎Unvailable due to ‎‏‎‎‏‏‎<xliff:g id="REASON">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎Open ‎‏‎‎‏‏‎<xliff:g id="ID_1">%s</xliff:g>‎‏‎‎‏‏‏‎ settings.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‎Edit order of settings.‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎Page ‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$d</xliff:g>‎‏‎‎‏‏‏‎ of ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 52b311b..35499f0 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Cambio de proveedor de red"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir detalles de la batería"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batería <xliff:g id="NUMBER">%d</xliff:g> por ciento"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Cargando batería: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Configuración del sistema"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificaciones"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Ver todas las notificaciones"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sin nombre"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Listo para transmitir"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No hay dispositivos disponibles"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"La red Wi-Fi no está conectada"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertir colores"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Accediste como <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Sin Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Abrir página de detalles"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"No disponible debido a <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir configuración de <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar orden de configuración"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 0329430..54364a5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Cambiando la red del operador"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir detalles de la batería"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> por ciento de batería"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batería cargando (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%)."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Ajustes del sistema"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificaciones"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Ver todas las notificaciones"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sin nombre"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Listo para enviar"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"No hay dispositivos disponibles"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi sin conexión"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertir colores"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Has iniciado sesión como <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Sin Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Abrir detalles."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"No disponible (<xliff:g id="REASON">%s</xliff:g>)"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir ajustes de <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Cambiar el orden de los ajustes."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 80313e7..90e9334 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Operaatori võrku muudetakse"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Aku üksikasjade avamine"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Aku: <xliff:g id="NUMBER">%d</xliff:g> protsenti."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Akut laetakse (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%)."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Süsteemiseaded"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Märguanded"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Kõikide märguannete kuvamine"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nimeta seade"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Valmis ülekandmiseks"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Ühtegi seadet pole saadaval"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"WiFi-ühendus puudub"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Heledus"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAATNE"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Vaheta värve"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Sisse logitud kasutajana <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Interneti-ühendus puudub"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Ava üksikasjad."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Põhjuse <xliff:g id="REASON">%s</xliff:g> tõttu pole saadaval"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ava teenuse <xliff:g id="ID_1">%s</xliff:g> seaded."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Muuda seadete järjestust."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Leht <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 9e19723..c6314e4 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Operadorearen sarea aldatzen"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Ireki bateriaren xehetasunak"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Bateriaren karga: <xliff:g id="NUMBER">%d</xliff:g>."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Kargatzen ari da bateria. %% <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> arte kargatu da oraingoz."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistemaren ezarpenak."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Jakinarazpenak."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Ikusi jakinarazpen guztiak"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Izenik gabeko gailua"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Igortzeko prest"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Ez dago gailurik erabilgarri"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Ez zaude konektatuta Wi-Fi sarera"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Distira"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIKOA"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Alderantzikatu koloreak"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> gisa hasi duzu saioa"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Ez dago Interneteko konexiorik"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Ireki xehetasunak."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Ez dago erabilgarri arrazoi honengatik: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ireki <xliff:g id="ID_1">%s</xliff:g> ezarpenak."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editatu ezarpenen ordena."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g> orria"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7ef324f..ca38030 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"دستگاه بدون نام"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"آماده برای فرستادن"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"دستگاهی موجود نیست"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"‏Wi-Fi وصل نیست"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"روشنایی"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"خودکار"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"برگردان رنگ‌ها"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"با <xliff:g id="ID_1">%s</xliff:g> به سیستم وارد شده‌اید"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"عدم اتصال به اینترنت"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"باز کردن جزئیات."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"به‌دلیل <xliff:g id="REASON">%s</xliff:g> دردسترس نیست"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"باز کردن تنظیمات <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ویرایش ترتیب تنظیمات."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"صفحه <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index dd82e08..412bee1 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nimetön laite"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Valmis lähetystä varten"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Laitteita ei ole käytettävissä"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fiä ei ole yhdistetty"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kirkkaus"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Käänteiset värit"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Kirjautunut tilillä <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Ei internetyhteyttä"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Avaa tiedot."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Ei käytettävissä, koska <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Avaa kohteen <xliff:g id="ID_1">%s</xliff:g> asetukset."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Muokkaa asetusten järjestystä."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Sivu <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index f9a9165..8821178 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Changer de réseau de fournisseur de services"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Ouvrir les détails de la pile"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Pile : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"La pile est en cours de charge : <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Paramètres système"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifications"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Afficher toutes les notifications"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Appareil sans nom"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Prêt à diffuser"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Aucun appareil à proximité"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Non connecté au Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosité"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverser les couleurs"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Connecté comme <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Aucune connexion Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Ouvrir les détails."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Non disponible pour la raison suivante : <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ouvrir les paramètres <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifier l\'ordre des paramètres."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 297f8f4..e37722a 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Modification du réseau de l\'opérateur"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Ouvrir les détails de la batterie"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batterie : <xliff:g id="NUMBER">%d</xliff:g> pour cent"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batterie en charge : <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Paramètres système"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifications"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Afficher toutes les notifications"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Appareil sans nom"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Prêt à caster"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Aucun appareil disponible."</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi non connecté"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosité"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverser les couleurs"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Connecté en tant que <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Aucun accès à Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Ouvrir les détails."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Indisponible pour la raison suivante : <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Ouvrir les paramètres <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifier l\'ordre des paramètres."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index bea5481..80f3c93 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Cambio de rede do operador"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir os detalles da batería"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Carga da batería: <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"A batería está cargando. Nivel: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Configuración do sistema"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificacións"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Ver todas as notificacións"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sen nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Listo para emitir"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Non hai dispositivos dispoñibles"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"A wifi non está conectada"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
@@ -403,9 +402,9 @@
     <string name="interruption_level_none_twoline" msgid="3957581548190765889">"Silencio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Só\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Só\nalarmas"</string>
-    <string name="keyguard_indication_charging_time" msgid="2056340799276374421">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> para rematar a carga)"</string>
-    <string name="keyguard_indication_charging_time_fast" msgid="7767562163577492332">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rapidamente (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> para rematar a carga)"</string>
-    <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> para rematar a carga)"</string>
+    <string name="keyguard_indication_charging_time" msgid="2056340799276374421">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> para completar a carga)"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="7767562163577492332">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rapidamente (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> para completar a carga)"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="3769655133567307069">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> para completar a carga)"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Cambiar usuario, usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Iniciaches sesión como <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Non hai conexión a Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Abrir detalles."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Opcións non-dispoñibles polo seguinte motivo: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir configuración de <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar a orde das opcións de configuración."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Páxina <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index fd2c102..cf5692b 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"કૅરીઅર નેટવર્કમાં ફેરફાર થઈ રહ્યો છે"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"બૅટરીની વિગતો ખોલો"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"બૅટરી <xliff:g id="NUMBER">%d</xliff:g> ટકા."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"બૅટરી ચાર્જ થઈ રહી છે, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ટકા."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"બૅટરી ચાર્જ થઈ રહી છે, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"સિસ્ટમ સેટિંગ્સ."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"નોટિફિકેશનો."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"બધી સૂચના જુઓ"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"અનામાંકિત ઉપકરણ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"કાસ્ટ કરવા માટે તૈયાર"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"કોઈ ઉપકરણો ઉપલબ્ધ નથી"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"વાઇ-ફાઇ કનેક્ટ નથી"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"તેજ"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"સ્વતઃ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"રંગોને ઉલટાવો"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> તમારી સ્ક્રીન પર જે પ્રદર્શિત થાય છે તે દરેક વસ્તુને કેપ્ચર કરવાનું પ્રારંભ કરશે."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"ફરીથી બતાવશો નહીં"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"બધુ સાફ કરો"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"મેનેજ કરો"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"ખલેલ પાડશો નહીં દ્વારા થોભાવેલ નોટિફિકેશન"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"હવે પ્રારંભ કરો"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"કોઈ સૂચનાઓ નથી"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> તરીકે સાઇન ઇન કર્યું"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"કોઈ ઇન્ટરનેટ નથી"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"વિગતો ખોલો."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>ને કારણે અનુપલબ્ધ છે"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> સેટિંગ્સ ખોલો."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"સેટિંગ્સનો ક્રમ સંપાદિત કરો."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> માંથી <xliff:g id="ID_1">%1$d</xliff:g> પૃષ્ઠ"</string>
@@ -855,10 +856,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"મંજૂરી આપો"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"નકારો"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"બૅટરી સેવર શેડ્યૂલ કરવા માટે ટૅપ કરો"</string>
-    <string name="auto_saver_text" msgid="6324376061044218113">"બૅટરીનું સ્તર <xliff:g id="PERCENTAGE">%d</xliff:g>%% પર હોય ત્યારે આપમેળે ચાલુ કરો"</string>
+    <string name="auto_saver_text" msgid="6324376061044218113">"બૅટરીનું સ્તર <xliff:g id="PERCENTAGE">%d</xliff:g>%% પર હોય ત્યારે ઑટોમૅટિક રીતે ચાલુ કરો"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"ના, આભાર"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"બૅટરી સેવર શેડ્યૂલ ચાલુ થયું"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"બૅટરીનું સ્તર એકવાર <xliff:g id="PERCENTAGE">%d</xliff:g>%% કરતાં ઓછું થાય તે પછી બૅટરી સેવર આપમેળે ચાલુ થશે."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"બૅટરીનું સ્તર એકવાર <xliff:g id="PERCENTAGE">%d</xliff:g>%% કરતાં ઓછું થાય તે પછી બૅટરી સેવર ઑટોમૅટિક રીતે ચાલુ થશે."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"સેટિંગ"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"સમજાઈ ગયું"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index bf77c0d..ac34e9a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी का नेटवर्क बदल रहा है"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"बैटरी का विवरण खोलें"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> प्रति‍शत बैटरी."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"बैटरी चार्ज हो रही है, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> प्रतिशत."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"बैटरी चार्ज हो रही है, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"सिस्टम सेटिंग."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"सूचनाएं."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"पूरी सूचनाएं देखें"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"अनाम डिवाइस"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"कास्ट करने के लिए तैयार"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"कोई डिवाइस उपलब्ध नहीं"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"वाई-फ़ाई कनेक्ट नहीं है"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"स्क्रीन की रोशनी"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वत:"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"रंग उलटें"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> के रूप में प्रवेश किया हुआ है"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"इंटरनेट कनेक्शन नहीं है"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"विवरण खोलें."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> की वजह से मौजूद नहीं है"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग खोलें."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिंग के क्रम को बदलें"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"पेज <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index cf54bef..5df6999 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -321,6 +321,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Uređaj bez naziva"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Spreman za emitiranje"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nema dostupnih uređaja"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi mreža nije povezana"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Svjetlina"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATSKI"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Zamjena boja"</string>
@@ -788,6 +789,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prijavljeni ste kao <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nema interneta"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otvaranje pojedinosti."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nije dostupno jer <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvaranje postavki za <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Uređivanje redoslijeda postavki."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -850,7 +852,7 @@
     <string name="running_foreground_services_title" msgid="381024150898615683">"Izvođenje aplikacija u pozadini"</string>
     <string name="running_foreground_services_msg" msgid="6326247670075574355">"Dodirnite da biste vidjeli pojedinosti o potrošnji baterije i podatkovnom prometu"</string>
     <string name="mobile_data_disable_title" msgid="1068272097382942231">"Želite li isključiti mobilne podatke?"</string>
-    <string name="mobile_data_disable_message" msgid="4756541658791493506">"Nećete imati pristup mobilnim podacima ili internetu putem operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem Wi-Fi-ja."</string>
+    <string name="mobile_data_disable_message" msgid="4756541658791493506">"Nećete imati pristup mobilnim podacima ili internetu putem operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem Wi-Fija."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"vaš mobilni operater"</string>
     <string name="touch_filtered_warning" msgid="8671693809204767551">"Budući da aplikacija prekriva zahtjev za dopuštenje, Postavke ne mogu potvrditi vaš odgovor."</string>
     <string name="slice_permission_title" msgid="7465009437851044444">"Želite li dopustiti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 534cabd..f09a8b3 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Név nélküli eszköz"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Küldésre kész"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nem áll rendelkezésre eszköz"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Nem kapcsolódik Wi‑Fi-hálózathoz"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Fényerő"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"automatikus"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Színek invertálása"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Bejelentkezve mint <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nincs internetkapcsolat"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"A részletek megnyitása."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nem áll rendelkezésre a következő ok miatt: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"A(z) <xliff:g id="ID_1">%s</xliff:g> beállításainak megnyitása."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Beállítások sorrendjének szerkesztése."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. oldal, összesen: <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 3b4e773..8973ecf 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Անանուն սարք"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Պատրաստ է հեռարձակման"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Հասանելի սարքեր չկան"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi-ը միացված չէ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Պայծառություն"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Ավտոմատ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Շրջել գույները"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Մուտք է գործել որպես <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Ինտերնետ կապ չկա"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Բացել մանրամասները:"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Անհասանելի է, քանի որ <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Բացել <xliff:g id="ID_1">%s</xliff:g> կարգավորումները:"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Խմբագրել կարգավորումների հերթականությունը:"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Էջ <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index ef5f2dc..2e3040c 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Perangkat tanpa nama"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Siap melakukan transmisi"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Perangkat tak tersedia"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi tidak terhubung"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversi warna"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Login sebagai <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Tidak ada internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Buka detail."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Tidak tersedia karena <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Buka setelan <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit urutan setelan."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> dari <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 7119cb1..fd15fc3 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Skiptir um farsímakerfi"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Opna upplýsingar um rafhlöðu"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> prósent á rafhlöðu."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Rafhlaða í hleðslu, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> prósent."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Rafhlaða í hleðslu, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Kerfisstillingar."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Tilkynningar."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Sjá allar tilkynningar"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Ónefnt tæki"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tilbúið í útsendingu"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Engin tæki til staðar"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi ekki tengt"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Birtustig"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"SJÁLFVIRKT"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Umsnúa litum"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Skráð(ur) inn sem <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Engin nettenging"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Opna upplýsingasíðu."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Ekki tiltækt vegna <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Opna <xliff:g id="ID_1">%s</xliff:g> stillingar."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Breyta röð stillinga."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Blaðsíða <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 99f413d..37fabb8 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Cambio della rete dell\'operatore"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Visualizza i dettagli relativi alla batteria"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batteria: <xliff:g id="NUMBER">%d</xliff:g> percento."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batteria in carica, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Impostazioni di sistema."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notifiche."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Visualizza tutte le notifiche"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo senza nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto a trasmettere"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nessun dispositivo disponibile"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Nessuna connessione Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosità"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverti colori"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Accesso eseguito come <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nessuna connessione a Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Apri i dettagli."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Non disponibile a causa di un problema <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Apri le impostazioni <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifica l\'ordine delle impostazioni."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> di <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ad2003d..0d60e6c 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -33,7 +33,7 @@
     </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"אין הודעות"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"מתמשך"</string>
-    <string name="status_bar_latest_events_title" msgid="6594767438577593172">"הודעות"</string>
+    <string name="status_bar_latest_events_title" msgid="6594767438577593172">"התראות"</string>
     <string name="battery_low_title" msgid="9187898087363540349">"ייתכן שהסוללה תתרוקן בקרוב"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"נותרו <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"נותרו <xliff:g id="PERCENTAGE">%1$s</xliff:g>, נשארו בערך <xliff:g id="TIME">%2$s</xliff:g> על סמך השימוש במכשיר"</string>
@@ -51,7 +51,7 @@
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"סיבוב אוטומטי של המסך"</string>
     <string name="status_bar_settings_mute_label" msgid="554682549917429396">"השתק"</string>
     <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"אוטומטי"</string>
-    <string name="status_bar_settings_notifications" msgid="397146176280905137">"הודעות"</string>
+    <string name="status_bar_settings_notifications" msgid="397146176280905137">"התראות"</string>
     <string name="bluetooth_tethered" msgid="7094101612161133267">"‏Bluetooth קשור"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"הגדר שיטות קלט"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"מקלדת פיזית"</string>
@@ -176,11 +176,11 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"רשת ספק משתנה"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"פתיחת פרטי סוללה"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> אחוזים של סוללה."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"טעינת סוללה, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> אחוז."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"טעינת סוללה, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"הגדרות מערכת"</string>
-    <string name="accessibility_notifications_button" msgid="4498000369779421892">"הודעות"</string>
+    <string name="accessibility_notifications_button" msgid="4498000369779421892">"התראות"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"הצגת כל ההודעות"</string>
-    <string name="accessibility_remove_notification" msgid="3603099514902182350">"נקה התראה"</string>
+    <string name="accessibility_remove_notification" msgid="3603099514902182350">"מחיקת התראה"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"‏GPS מופעל."</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"‏השגת GPS."</string>
     <string name="accessibility_tty_enabled" msgid="4613200365379426561">"‏TeleTypewriter מופעל"</string>
@@ -195,7 +195,7 @@
     <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"כל האפליקציות האחרונות נסגרו."</string>
     <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"פתח מידע על האפליקציה <xliff:g id="APP">%s</xliff:g>."</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"מפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
-    <string name="accessibility_notification_dismissed" msgid="854211387186306927">"הודעה נדחתה."</string>
+    <string name="accessibility_notification_dismissed" msgid="854211387186306927">"התראה נדחתה."</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"לוח התראות."</string>
     <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"הגדרות מהירות."</string>
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"מסך נעילה."</string>
@@ -323,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"מכשיר ללא שם"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"מוכן להעביר"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"אין מכשירים זמינים"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"‏אין חיבור ל-Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"בהירות"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"אוטומטי"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"היפוך צבעים"</string>
@@ -342,7 +343,7 @@
       <item quantity="other">‏%d מכשירים</item>
       <item quantity="one">מכשיר אחד</item>
     </plurals>
-    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"הודעות"</string>
+    <string name="quick_settings_notifications_label" msgid="4818156442169154523">"התראות"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"פנס"</string>
     <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"חבילת גלישה"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"שימוש בנתונים"</string>
@@ -542,7 +543,7 @@
     <string name="stream_ring" msgid="8213049469184048338">"צלצול"</string>
     <string name="stream_music" msgid="9086982948697544342">"מדיה"</string>
     <string name="stream_alarm" msgid="5209444229227197703">"שעון מעורר"</string>
-    <string name="stream_notification" msgid="2563720670905665031">"הודעה"</string>
+    <string name="stream_notification" msgid="2563720670905665031">"התראה"</string>
     <string name="stream_bluetooth_sco" msgid="2055645746402746292">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="2447177903892477915">"‏טון זוגי מרובה תדרים (DTMF)"</string>
     <string name="stream_accessibility" msgid="301136219144385106">"נגישות"</string>
@@ -614,8 +615,8 @@
     <string name="tuner_full_importance_settings" msgid="3207312268609236827">"פקדים של הודעות הפעלה"</string>
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"פועל"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"כבוי"</string>
-    <string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של הודעות הפעלה, תוכל להגדיר רמת חשיבות מ-0 עד 5 להודעות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצג בראש רשימת ההודעות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתר ממסך הנעילה ומשורת הסטטוס \n- הצג בתחתית רשימת ההודעות \n\n"<b>"רמה 0"</b>" \n- חסום את כל ההודעות מהאפליקציה"</string>
-    <string name="notification_header_default_channel" msgid="7506845022070889909">"הודעות"</string>
+    <string name="power_notification_controls_description" msgid="4372459941671353358">"בעזרת פקדים של התראות הפעלה, אפשר להגדיר רמת חשיבות מ-0 עד 5 להתראות אפליקציה. \n\n"<b>"רמה 5"</b>" \n- הצגה בראש רשימת ההודעות \n- אפשר הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 4"</b>" \n- מנע הפרעה במסך מלא \n- תמיד אפשר הצצה \n\n"<b>"רמה 3"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n\n"<b>"רמה 2"</b>" \n- מנע הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n\n"<b>"רמה 1"</b>" \n- מניעת הפרעה במסך מלא \n- אף פעם אל תאפשר הצצה \n- אף פעם אל תאפשר קול ורטט \n- הסתרה ממסך הנעילה ומשורת הסטטוס \n- הצגה בתחתית רשימת ההתראות \n\n"<b>"רמה 0"</b>" \n- חסימה את כל ההתראות מהאפליקציה"</string>
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"התראות"</string>
     <string name="notification_channel_disabled" msgid="344536703863700565">"ההודעות האלה לא יוצגו לך יותר"</string>
     <string name="notification_channel_minimized" msgid="1664411570378910931">"ההודעות האלה ימוזערו"</string>
     <string name="inline_blocking_helper" msgid="3055064577771478591">"הודעות אלה בדרך כלל נדחות על ידיך. \nלהמשיך להציג אותן?"</string>
@@ -642,7 +643,7 @@
     <string name="notification_done" msgid="5279426047273930175">"סיום"</string>
     <string name="inline_undo" msgid="558916737624706010">"ביטול"</string>
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
-    <string name="notification_menu_gear_description" msgid="2204480013726775108">"בקרת הודעות"</string>
+    <string name="notification_menu_gear_description" msgid="2204480013726775108">"בקרת התראות"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"אפשרויות של דחיית הודעות לטיפול בהמשך"</string>
     <string name="notification_menu_snooze_action" msgid="1112254519029621372">"הפעלת נודניק"</string>
     <string name="snooze_undo" msgid="6074877317002985129">"ביטול"</string>
@@ -693,7 +694,7 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"דף הבית"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"מהזמן האחרון"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"הקודם"</string>
-    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"הודעות"</string>
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"התראות"</string>
     <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"מקשי קיצור במקלדת"</string>
     <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"החלפה של פריסת מקלדת"</string>
     <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"אפליקציות"</string>
@@ -782,7 +783,7 @@
     <string name="accessibility_qs_edit_tile_add" msgid="3520406665865985109">"הוספת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_move" msgid="3108103090006972938">"העברת <xliff:g id="TILE_NAME">%1$s</xliff:g> למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"עורך הגדרות מהירות."</string>
-    <string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"הודעת <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
+    <string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="dock_forced_resizable" msgid="5914261505436217520">"ייתכן שהיישום לא יפעל עם מסך מפוצל."</string>
     <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"האפליקציה אינה תומכת במסך מפוצל."</string>
     <string name="forced_resizable_secondary_display" msgid="4230857851756391925">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
@@ -794,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"מחובר בתור <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"אין אינטרנט"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"פתיחת פרטים."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"לא זמין כי <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"פתיחת הגדרות של <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"עריכת סדר ההגדרות."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"דף <xliff:g id="ID_1">%1$d</xliff:g> מתוך <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 81870e5..c651f9e 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"携帯通信会社のネットワークを変更します"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"電池の詳細情報を開きます"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"電池残量: <xliff:g id="NUMBER">%d</xliff:g>パーセント"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"電池充電中: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"システム設定。"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"通知をすべて表示"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"名前のないデバイス"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"キャスト準備完了"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"利用可能なデバイスがありません"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi に接続されていません"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"画面の明るさ"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"色を反転"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> としてログインします"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"インターネットに接続されていません"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"詳細情報を開きます。"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"利用できない理由: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> の設定を開きます。"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"設定の順序を編集します。"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ページ <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 7e71fbe..f91ffb0 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"ოპერატორის ქსელის შეცვლა"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"ბატარეის დეტალების გახსნა"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"ბატარეა: <xliff:g id="NUMBER">%d</xliff:g> პროცენტი."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ბატარეა იტენება, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> პროცენტი."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ბატარეა იტენება, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"სისტემის პარამეტრები."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"შეტყობინებები"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"ყველა შეტყობინების ნახვა"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"უსახელო მოწყობილობა"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"მზად არის სამაუწყებლოდ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"მოწყობილობები მიუწვდომელია"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi არ არის დაკავშირებული"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"განათება"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ავტომატურად"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ფერების შებრუნება"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"შესული ხართ, როგორც <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ინტერნეტ-კავშირი არ არის"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"დეტალების გახსნა."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"მიუწვდომელია, რადგან <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> პარამეტრების გახსნა."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"პარამეტრების მიმდევრობის რედაქტირება."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"გვერდი <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>-დან"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 89c7ff8..b79ea7b 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Оператор желісін өзгерту"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Батарея мәліметтерін ашу"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Батарея <xliff:g id="NUMBER">%d</xliff:g> пайыз."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батарея зарядталуда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> пайыз."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батарея зарядталуда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Жүйе параметрлері."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Хабарлар."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Барлық хабарландыруды қарау"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Атаусыз құрылғы"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Трансляциялауға дайын"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Құрылғылар қол жетімді емес"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi желісіне жалғанбаған"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Жарықтығы"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Авто"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Түстерді инверсиялау"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ретінде кірдіңіз"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Интернетпен байланыс жоқ"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Мәліметтерді ашу."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Қолжетімді емес, өйткені <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> параметрлерін ашу."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Параметрлер тәртібін өзгерту."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ішінен <xliff:g id="ID_1">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 4d0c97c..0731a5e 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ឧបករណ៍​​ដែល​មិន​មាន​ឈ្មោះ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ត្រៀម​រួចរាល់​ដើម្បី​ចាត់​ថ្នាក់"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"មិន​មាន​ឧបករណ៍​ដែល​អាច​ប្រើ​បាន"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"មិនមាន​ការតភ្ជាប់ Wi-Fi ទេ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ពន្លឺ"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ស្វ័យប្រវត្តិ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ដាក់​​​បញ្ច្រាស​ពណ៌"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"បានចូលជា <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"គ្មាន​អ៊ីនធឺណិតទេ"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"បើកព័ត៌មានលម្អិត"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"មិនអាច​ប្រើបានទេ ដោយសារ <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"បើការកំណត់ <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"កែលំដាប់ការកំណត់"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ទំព័រ <xliff:g id="ID_1">%1$d</xliff:g> នៃ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 45f6944..c4c642f 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"ವಾಹಕ ನೆಟ್‌ವರ್ಕ್ ಬದಲಾಯಿಸುವಿಕೆ"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"ಬ್ಯಾಟರಿ ವಿವರಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"ಬ್ಯಾಟರಿ <xliff:g id="NUMBER">%d</xliff:g> ಪ್ರತಿಶತ."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ಬ್ಯಾಟರಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ಪ್ರತಿಶತ."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ಬ್ಯಾಟರಿ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ಪ್ರತಿಶತ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"ಸಿಸ್ಟಂ ಸೆಟ್ಟಿಂಗ್‌ಗಳು."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"ಅಧಿಸೂಚನೆಗಳು."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಿ"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ಹೆಸರಿಸದಿರುವ ಸಾಧನ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ಬಿತ್ತರಿಸಲು ಸಿದ್ದವಾಗಿದೆ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ಯಾವುದೇ ಸಾಧನಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ಪ್ರಕಾಶಮಾನ"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ಸ್ವಯಂ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ಬಣ್ಣಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ಅವರಂತೆ ಸೈನ್ ಇನ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ವಿವರಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> ಕಾರಣದಿಂದಾಗಿ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಕ್ರಮವನ್ನು ಎಡಿಟ್ ಮಾಡಿ."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="ID_1">%1$d</xliff:g> ಪುಟ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index dfa2bb4..9014880 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"이동통신사 네트워크 변경"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"배터리 세부정보 열기"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"배터리 <xliff:g id="NUMBER">%d</xliff:g>퍼센트"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"배터리 충전 중, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%입니다."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"시스템 설정"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"알림"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"모든 알림 보기"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"이름이 없는 기기"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"전송 준비 완료"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"사용 가능한 기기가 없습니다."</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi가 연결되어 있지 않음"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"밝기"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"자동"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"색상 반전"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g>(으)로 로그인됨"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"인터넷 연결 없음"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"세부정보 열기"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"다음 이유로 사용할 수 없음: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> 설정 열기"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"설정 순서 수정"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>페이지 중 <xliff:g id="ID_1">%1$d</xliff:g>페이지"</string>
@@ -856,14 +856,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"허용"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"거부"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"탭하여 배터리 세이버 예약"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_text (6324376061044218113) -->
-    <skip />
+    <string name="auto_saver_text" msgid="6324376061044218113">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>%%가 되면 자동으로 켜기"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"사용 안함"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"배터리 세이버 예약 사용 설정됨"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
-    <skip />
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 아래로 내려가면 배터리 세이버가 자동으로 켜집니다."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"설정"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"확인"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index fc8a7d2..51cb1b1 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Байланыш оператору өзгөртүлүүдө"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Батареянын чоо-жайын ачуу"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Батарея <xliff:g id="NUMBER">%d</xliff:g> пайыз."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батарея кубатталууда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> пайыз."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батарея кубатталууда, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Система тууралоолору."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Билдирмелер"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Бардык эскертмелерди көрүү"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Аты жок түзмөк"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Тышкы экранга чыгарууга даяр"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Жеткиликтүү түзмөктөр жок"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi туташкан жок"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Жарыктыгы"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТО"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Түстөрдү инверсиялоо"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> аккаунту менен кирди"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Интернет жок"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Чоо-жайын ачуу."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> себебине байланыштуу жеткиликсиз"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> жөндөөлөрүн ачуу."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Жөндөөлөрдүн иретин өзгөртүү."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ичинен <xliff:g id="ID_1">%1$d</xliff:g>-бет"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1b8adcc..d442258 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"​ອຸ​ປະ​ກອນບໍ່​ມີ​ຊື່"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"​ພ້ອ​ມ​ສົ່ງ​ສັນ​ຍານ​ແລ້ວ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"​ບໍ່​ມີ​ອຸ​ປະ​ກອນ​ທີ່​ສາ​ມາດ​ໃຊ້​ໄດ້"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ບໍ່ໄດ້ເຊື່ອມຕໍ່ Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ຄວາມແຈ້ງ"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ອັດຕະໂນມັດ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ສະຫຼັບສີ"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"ເຂົ້າສູ່ລະບົບເປັນ <xliff:g id="ID_1">%s</xliff:g> ແລ້ວ"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ບໍ່ມີອິນເຕີເນັດ"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ເປີດລາຍລະອຽດ."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"ບໍ່ສາມາດໃຊ້ໄດ້ເນື່ອງຈາກ <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"ເປີດການຕັ້ງຄ່າ <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ແກ້ໄຂລຳດັບການຕັ້ງຄ່າ."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 2dccd89c..e26df8b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -176,7 +176,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Keičiamas operatoriaus tinklas"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Atidaryti išsamią akumuliatoriaus informaciją"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Akumuliatorius: <xliff:g id="NUMBER">%d</xliff:g> proc."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Įkraunamas akumuliatorius, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> proc."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Įkraunamas akumuliatorius, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistemos nustatymai"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Pranešimai."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Žr. visus pranešimus"</string>
@@ -323,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Įrenginys be pavadinimo"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Paruošta perduoti"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nėra pasiekiamų įrenginių"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"„Wi-Fi“ neprijungtas"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Šviesumas"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Pakeisti spalvas"</string>
@@ -794,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prisijungta kaip <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nėra interneto ryšio"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Atidaryti išsamią informaciją."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nepasiekiama dėl <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Atidaryti „<xliff:g id="ID_1">%s</xliff:g>“ nustatymus."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Redaguoti nustatymų tvarką."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g> psl. iš <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -866,10 +868,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Leisti"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Neleisti"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Palietę planuokite akumuliatoriaus tausojimo priemonės veikimą"</string>
-    <string name="auto_saver_text" msgid="6324376061044218113">"Įjunkite automatiškai akumuliatoriaus įkrovai pasiekus <xliff:g id="PERCENTAGE">%d</xliff:g> proc."</string>
+    <string name="auto_saver_text" msgid="6324376061044218113">"Įjunkite automatiškai akumuliatoriaus įkrovai pasiekus <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, ačiū"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Akumuliatoriaus tausojimo priemonės veikimas suplanuotas"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Akumuliatoriaus tausojimo priemonė bus įjungta automatiškai akumuliatoriaus įkrovai pasiekus mažiau nei <xliff:g id="PERCENTAGE">%d</xliff:g> proc."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Akumuliatoriaus tausojimo priemonė bus įjungta automatiškai akumuliatoriaus įkrovai pasiekus mažiau nei <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Nustatymai"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"Supratau"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Pat. „SysUI“ krūvą"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 7c906ca..6f5e230 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -175,7 +175,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Mobilo sakaru operatora tīkla mainīšana"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Atvērt akumulatora informāciju"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Akumulators: <xliff:g id="NUMBER">%d</xliff:g> procenti"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Notiek akumulatora uzlāde, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procenti."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Notiek akumulatora uzlāde, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistēmas iestatījumi"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Paziņojumi"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Skatīt visus paziņojumus"</string>
@@ -321,6 +321,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nenosaukta ierīce"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gatavs apraidei"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nav pieejamu ierīču."</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Nav izveidots savienojums ar Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Spilgtums"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMĀTISKI"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertēt krāsas"</string>
@@ -788,6 +789,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Pierakstījies kā <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nav piekļuves internetam"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Atvērt detalizēto informāciju."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nav pieejams šāda iemesla dēļ: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Atvērt <xliff:g id="ID_1">%s</xliff:g> iestatījumus."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Rediģēt iestatījumu secību."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. lpp. no <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -860,14 +862,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Atļaut"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Neatļaut"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Pieskarieties, lai iestatītu akumulatora jaudas taupīšanas režīma grafiku"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_text (6324376061044218113) -->
-    <skip />
+    <string name="auto_saver_text" msgid="6324376061044218113">"Ieslēgt automātiski, kad akumulatora uzlādes līmenis ir <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Nē, paldies"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Ieslēgts akumulatora enerģijas taupīšanas režīma grafiks"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
-    <skip />
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Tiklīdz akumulatora uzlādes līmenis būs zemāks nekā <xliff:g id="PERCENTAGE">%d</xliff:g>%%, tiks automātiski ieslēgts akumulatora jaudas taupīšanas režīms."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Iestatījumi"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"Labi"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a5ffd0f..d13193b7 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Променување на мрежата на операторот"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Отвори ги деталите за батеријата"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Батерија <xliff:g id="NUMBER">%d</xliff:g> проценти."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Полнење на батеријата, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> проценти."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Полнење на батеријата, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Поставки на систем."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Известувања"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Видете ги сите известувања"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Неименуван уред"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Подготвено за емитување"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Нема достапни уреди"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi не е поврзано"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Осветленост"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Автоматски"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Преврти ги боите"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Најавени сте како <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Нема интернет"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Отворете ги деталите."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Недостапно бидејќи <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Отворете ги поставките на <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Уредете го редоследот на поставките."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Страница <xliff:g id="ID_1">%1$d</xliff:g> од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 5abde4e..a5cad4e 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"കാരിയർ നെറ്റ്‌വർക്ക് മാറ്റൽ"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"ബാറ്ററി വിശദാംശങ്ങൾ തുറക്കുക"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"ബാറ്ററി <xliff:g id="NUMBER">%d</xliff:g> ശതമാനം."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ബാറ്ററി ചാർജുചെയ്യുന്നു, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ശതമാനം."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ബാറ്ററി ചാർജുചെയ്യുന്നു, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"സിസ്‌റ്റം ക്രമീകരണങ്ങൾ."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"അറിയിപ്പുകൾ."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"എല്ലാ അറിയിപ്പുകളും കാണുക"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"പേരിടാത്ത ഉപകരണം"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"കാസ്‌റ്റ് ചെയ്യാൻ തയ്യാറാണ്"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ഉപകരണങ്ങളൊന്നും ലഭ്യമല്ല"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"വൈഫൈ കണക്റ്റ് ചെയ്‌തിട്ടില്ല"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"തെളിച്ചം"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"യാന്ത്രികം"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"നിറം മാറ്റുക"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"നിങ്ങളുടെ സ്ക്രീനിൽ പ്രദർശിപ്പിച്ചിരിക്കുന്ന എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ക്യാപ്‌ചർ ചെയ്യുന്നത് ആരംഭിക്കും."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"വീണ്ടും കാണിക്കരുത്"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"എല്ലാം മായ്‌ക്കുക"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"മാനേജ് ചെയ്യുക"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'ശല്യപ്പെടുത്തരുത്\' വഴി അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തി"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ആയി സൈൻ ഇൻ ചെയ്‌തു"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ഇന്റർനെറ്റ് ഇല്ല"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"വിശദാംശങ്ങൾ തുറക്കുക."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> എന്ന കാരണത്താൽ ലഭ്യമല്ല"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ക്രമീകരണം തുറക്കുക."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ക്രമീകരണ ക്രമം എഡിറ്റുചെയ്യുക."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"പേജ് <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -858,7 +859,7 @@
     <string name="auto_saver_text" msgid="6324376061044218113">"ബാറ്ററി <xliff:g id="PERCENTAGE">%d</xliff:g>%% ആകുമ്പോൾ സ്വമേധയാ ഓണാക്കുക"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"വേണ്ട"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"ബാറ്ററി ലാഭിക്കൽ ഷെഡ്യൂൾ ഓണാക്കുക"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"ബാറ്ററി <xliff:g id="PERCENTAGE">%d</xliff:g>%%-ൽ താഴെയാകുമ്പോൾ, ബാറ്ററി ലാഭിക്കൽ സ്വമേധയാ ഓണാകും."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"ബാറ്ററി <xliff:g id="PERCENTAGE">%d</xliff:g>%% ൽ താഴെയാകുമ്പോൾ, ബാറ്ററി ലാഭിക്കൽ സ്വമേധയാ ഓണാകും."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"ക്രമീകരണം"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"മനസ്സിലായി"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"SysUI ഹീപ്പ് ഡമ്പ് ചെയ്യുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 878e404..978fc0e 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -172,7 +172,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Оператор компанийн сүлжээг өөрчилж байна"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Тэжээлийн дэлгэрэнгүй мэдээллийг нээх"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Батерей <xliff:g id="NUMBER">%d</xliff:g> хувьтай."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Тэжээлийг цэнэглэж байна, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> хувь."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батарейг цэнэглэж байна, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Системийн тохиргоо."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Мэдэгдэл."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Бүх мэдэгдлийг харах"</string>
@@ -317,6 +317,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Нэргүй төхөөрөмж"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Дамжуулахад бэлэн"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Төхөөрөмж байхгүй"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi-д холбогдоогүй байна"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Тодрол"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОМАТ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Өнгийг урвуулах"</string>
@@ -780,6 +781,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g>-р нэвтэрсэн"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Интернэт алга"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Дэлгэрэнгүй мэдээллийг нээнэ үү."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>-н улмаас боломжгүй байна"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> тохиргоог нээнэ үү."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Тохиргооны дарааллыг өөрчилнө үү."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>-н <xliff:g id="ID_1">%1$d</xliff:g>-р хуудас"</string>
@@ -852,10 +854,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Зөвшөөрөх"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Татгалзах"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Тэжээл хэмнэгч онцлогийг хуваарилахын тулд товших"</string>
-    <string name="auto_saver_text" msgid="6324376061044218113">"Батерей <xliff:g id="PERCENTAGE">%d</xliff:g>%% болох үед автоматаар асаах"</string>
+    <string name="auto_saver_text" msgid="6324376061044218113">"Батарей <xliff:g id="PERCENTAGE">%d</xliff:g>%% болох үед автоматаар асаах"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Үгүй, баярлалаа"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Тэжээл хэмнэгч онцлогийн хуваарийг асаасан"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Батерей <xliff:g id="PERCENTAGE">%d</xliff:g>%%-с бага болсон үед Тэжээл хэмнэгч онцлог автоматаар асна."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Батарей <xliff:g id="PERCENTAGE">%d</xliff:g>%%-с бага болсон үед Тэжээл хэмнэгч автоматаар асна."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Тохиргоо"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"Ойлголоо"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 5a8e022..103ca46 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -57,7 +57,7 @@
     <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="APPLICATION">%1$s</xliff:g> ला <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> अॅक्सेस करण्याची अनुमती द्यायची का?"</string>
     <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> हाताळण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे का?"</string>
     <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> हाताळण्यासाठी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडायचे का?"</string>
-    <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इंस्टॉल केलेले अॅप्स या USB उपसाधनासह कार्य करत नाहीत. <xliff:g id="URL">%1$s</xliff:g> येथे या उपसाधनाविषयी अधिक जाणून घ्या"</string>
+    <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"इंस्टॉल केलेली अॅप्स या USB उपसाधनासह कार्य करत नाहीत. <xliff:g id="URL">%1$s</xliff:g> येथे या उपसाधनाविषयी अधिक जाणून घ्या"</string>
     <string name="title_usb_accessory" msgid="4966265263465181372">"USB उपसाधन"</string>
     <string name="label_view" msgid="6304565553218192990">"पहा"</string>
     <string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> कनेक्ट केलेली असताना नेहमी <xliff:g id="APPLICATION">%1$s</xliff:g> उघडा"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"निनावी डिव्हाइस"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"कास्ट करण्यास तयार"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"कोणतेही डिव्हाइसेस उपलब्ध नाहीत"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"वाय-फाय कनेक्ट केलेले नाही"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"चमक"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वयंचलित"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"रंगांचा क्रम उलटा लावा"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> आपल्‍या स्‍क्रीनवर प्रदर्शित होणारी प्रत्‍येक गोष्‍ट कॅप्‍चर करणे प्रारंभ करेल."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"पुन्हा दर्शवू नका"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"सर्व साफ करा"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"व्यवस्थापित करा"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"व्यत्यय आणून नकाद्वारे सूचना थांबवल्या"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"आता सुरू करा"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"सूचना नाहीत"</string>
@@ -486,7 +486,7 @@
     <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"तुमचे वैयक्तिक प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जे ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या नेटवर्क क्रियाकलापाचे परीक्षण करू शकते."</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"तुमचे डिव्हाइस <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ने व्यवस्थापित केले आहे."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"तुमचे डिव्हाइस व्यवस्थापित करण्यासाठी <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> वापरते."</string>
-    <string name="monitoring_description_do_body" msgid="3639594537660975895">"आपला प्रशासक सेटिंग्ज, कॉर्पोरेट प्रवेश, अॅप्स, आपल्या डिव्हाइशी संबंधित डेटा आणि डिव्हाइसच्या स्थान माहितीचे निरीक्षण आणि व्यवस्थापन करू शकतो."</string>
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"तुमचा प्रशासक सेटिंग्ज, कॉर्पोरेट प्रवेश, अॅप्स, आपल्या डिव्हाइशी संबंधित डेटा आणि डिव्हाइसच्या स्थान माहितीचे निरीक्षण आणि व्यवस्थापन करू शकतो."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"अधिक जाणून घ्या"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"तुम्ही <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
@@ -496,7 +496,7 @@
     <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"विश्वासू क्रेडेंशियल उघडा"</string>
     <string name="monitoring_description_network_logging" msgid="7223505523384076027">"आपल्या प्रशासकाने नेटवर्क लॉगिंग चालू केले आहे, जे आपल्या डिव्हाइसवरील रहदारीचे निरीक्षण करते.\n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"तुम्ही VPN कनेक्शन सेट करण्यासाठी अ‍ॅपला परवानगी दिली.\n\nहा अ‍ॅप ईमेल, अ‍ॅप्स आणि वेबसाइटसह, तुमच्या डिव्हाइस आणि नेटवर्क अॅक्टिव्हिटीचे परीक्षण करू शकतो."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"तुमचे कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले जाते.\n\nआपला प्रशासक ईमेल, अॅप्स आणि वेबसाइटसह आपल्या नेटवर्क अॅक्टिव्हिटीचे निरीक्षण करण्यास सक्षम आहे.\n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा.\n\nतुम्ही VPN शी देखील कनेक्ट आहात, जे आपल्या नेटवर्क अॅक्टिव्हिटीचे निरीक्षण करू शकते."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"तुमचे कार्य प्रोफाइल <xliff:g id="ORGANIZATION">%1$s</xliff:g> द्वारे व्यवस्थापित केले जाते.\n\nतुमचा प्रशासक ईमेल, अॅप्स आणि वेबसाइटसह आपल्या नेटवर्क अॅक्टिव्हिटीचे निरीक्षण करण्यास सक्षम आहे.\n\nअधिक माहितीसाठी आपल्या प्रशासकाशी संपर्क साधा.\n\nतुम्ही VPN शी देखील कनेक्ट आहात, जे आपल्या नेटवर्क अॅक्टिव्हिटीचे निरीक्षण करू शकते."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
     <string name="monitoring_description_app" msgid="1828472472674709532">"तुम्ही <xliff:g id="APPLICATION">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जे ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या नेटवर्क क्रियाकलापाचे परीक्षण करू शकते."</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"तुम्ही <xliff:g id="APPLICATION">%1$s</xliff:g> शी कनेक्‍ट केले आहे, जो ईमेल, अ‍ॅप्स आणि वेबसाइटसह आपल्‍या वैयक्तिक नेटवर्क क्रियाकलापाचे परीक्षण करू शकतो."</string>
@@ -577,8 +577,8 @@
     <string name="status_bar_airplane" msgid="7057575501472249002">"विमान मोड"</string>
     <string name="add_tile" msgid="2995389510240786221">"टाइल जोडा"</string>
     <string name="broadcast_tile" msgid="3894036511763289383">"प्रसारण टाइल"</string>
-    <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"तुम्ही त्यापूर्वी हे बंद केल्याशिय आपला पुढील <xliff:g id="WHEN">%1$s</xliff:g> होणारा अलार्म ऐकणार नाही"</string>
-    <string name="zen_alarm_warning" msgid="444533119582244293">"तुम्ही आपला <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
+    <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"तुम्ही त्यापूर्वी हे बंद केल्याशिय तुमचा पुढील <xliff:g id="WHEN">%1$s</xliff:g> होणारा अलार्म ऐकणार नाही"</string>
+    <string name="zen_alarm_warning" msgid="444533119582244293">"तुम्ही तुमचा <xliff:g id="WHEN">%1$s</xliff:g> वाजता होणारा पुढील अलार्म ऐकणार नाही"</string>
     <string name="alarm_template" msgid="3980063409350522735">"<xliff:g id="WHEN">%1$s</xliff:g> वाजता"</string>
     <string name="alarm_template_far" msgid="4242179982586714810">"<xliff:g id="WHEN">%1$s</xliff:g> रोजी"</string>
     <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"द्रुत सेटिंग्ज, <xliff:g id="TITLE">%s</xliff:g>."</string>
@@ -598,7 +598,7 @@
     <string name="show_brightness" msgid="6613930842805942519">"द्रुत सेटिंग्जमध्‍ये चमक दर्शवा"</string>
     <string name="experimental" msgid="6198182315536726162">"प्रायोगिक"</string>
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लूटूथ सुरू करायचे?"</string>
-    <string name="enable_bluetooth_message" msgid="9106595990708985385">"आपला कीबोर्ड तुमच्या टॅबलेटसह कनेक्ट करण्यासाठी, तुम्ही प्रथम ब्लूटूथ चालू करणे आवश्यक आहे."</string>
+    <string name="enable_bluetooth_message" msgid="9106595990708985385">"तुमचा कीबोर्ड तुमच्या टॅबलेटसह कनेक्ट करण्यासाठी, तुम्ही प्रथम ब्लूटूथ चालू करणे आवश्यक आहे."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"चालू करा"</string>
     <string name="show_silently" msgid="6841966539811264192">"सूचना शांतपणे दर्शवा"</string>
     <string name="block" msgid="2734508760962682611">"सर्व सूचना ब्लॉक करा"</string>
@@ -607,7 +607,7 @@
     <string name="tuner_full_importance_settings" msgid="3207312268609236827">"पॉवर सूचना नियंत्रणे"</string>
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"चालू"</string>
     <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"बंद"</string>
-    <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- पूर्ण स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- पूर्ण स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
+    <string name="power_notification_controls_description" msgid="4372459941671353358">"पॉवर सूचना नियंत्रणांच्या साहाय्याने तुम्ही अॅप सूचनांसाठी 0 ते 5 असे महत्त्व स्तर सेट करू शकता. \n\n"<b>"स्तर 5"</b>" \n- सूचना सूचीच्या शीर्षस्थानी दाखवा \n- फुल स्क्रीन व्यत्ययास अनुमती द्या \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 4"</b>\n" - फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- नेहमी डोकावून पहा \n\n"<b>"स्तर 3"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n\n"<b>"स्तर 2"</b>" \n- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n\n"<b>"स्तर 1"</b>\n"- फुल स्क्रीन व्यत्ययास प्रतिबंधित करा \n- कधीही डोकावून पाहू नका \n- कधीही ध्वनी किंवा व्हायब्रेट करू नका \n- लॉक स्क्रीन आणि स्टेटस बार मधून लपवा \n- सूचना सूचीच्या तळाशी दर्शवा \n\n"<b>"स्तर 0"</b>" \n- अॅपमधील सर्व सूचना ब्लॉक करा"</string>
     <string name="notification_header_default_channel" msgid="7506845022070889909">"सूचना"</string>
     <string name="notification_channel_disabled" msgid="344536703863700565">"आता तुम्हाला या सूचना दिसणार नाहीत"</string>
     <string name="notification_channel_minimized" msgid="1664411570378910931">"या सूचना लहान केल्या जातील"</string>
@@ -754,16 +754,16 @@
   </string-array>
     <string name="other" msgid="4060683095962566764">"अन्य"</string>
     <string name="accessibility_divider" msgid="5903423481953635044">"विभाजित-स्क्रीन विभाजक"</string>
-    <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"डावी पूर्ण स्क्रीन"</string>
+    <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"डावी फुल स्क्रीन"</string>
     <string name="accessibility_action_divider_left_70" msgid="3612060638991687254">"डावी 70%"</string>
     <string name="accessibility_action_divider_left_50" msgid="1248083470322193075">"डावी 50%"</string>
     <string name="accessibility_action_divider_left_30" msgid="543324403127069386">"डावी 30%"</string>
-    <string name="accessibility_action_divider_right_full" msgid="4639381073802030463">"उजवी पूर्ण स्क्रीन"</string>
-    <string name="accessibility_action_divider_top_full" msgid="5357010904067731654">"शीर्ष पूर्ण स्क्रीन"</string>
+    <string name="accessibility_action_divider_right_full" msgid="4639381073802030463">"उजवी फुल स्क्रीन"</string>
+    <string name="accessibility_action_divider_top_full" msgid="5357010904067731654">"शीर्ष फुल स्क्रीन"</string>
     <string name="accessibility_action_divider_top_70" msgid="5090779195650364522">"शीर्ष 70%"</string>
     <string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"शीर्ष 50%"</string>
     <string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"शीर्ष 10"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"तळाशी पूर्ण स्क्रीन"</string>
+    <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"तळाशी फुल स्क्रीन"</string>
     <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"स्थिती <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. संपादित करण्यासाठी दोनदा टॅप करा."</string>
     <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> . जोडण्यासाठी दोनदा टॅप करा."</string>
     <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> हलवा"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> म्हणून साइन इन केले"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"इंटरनेट नाही"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"तपशील उघडा."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> मुळे उपलब्ध नाही"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग्ज उघडा."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिंग्जचा क्रम संपादित करा."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"पृष्ठ <xliff:g id="ID_2">%2$d</xliff:g> पैकी <xliff:g id="ID_1">%1$d</xliff:g>"</string>
@@ -800,11 +801,11 @@
     <string name="pip_skip_to_next" msgid="1948440006726306284">"डावलून पुढे जा"</string>
     <string name="pip_skip_to_prev" msgid="1955311326688637914">"डावलून मागे जा"</string>
     <string name="thermal_shutdown_title" msgid="4458304833443861111">"तापल्‍यामुळे फोन बंद झाला"</string>
-    <string name="thermal_shutdown_message" msgid="9006456746902370523">"आपला फोन आता व्‍यवस्थित चालू आहे"</string>
-    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"आपला फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. आपला फोन आता व्‍यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास आपला फोन खूप तापेल:\n	•संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n	•मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n	•उच्च तापमानामध्ये आपला फोन वापरणे"</string>
+    <string name="thermal_shutdown_message" msgid="9006456746902370523">"तुमचा फोन आता व्‍यवस्थित चालू आहे"</string>
+    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्‍यवस्थित चालू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n	•संसाधन केंद्रित अॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अॅप यासारखे)\n	•मोठ्या फायली डाउनलोड किंवा अपलोड करणे\n	•उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
     <string name="high_temp_title" msgid="4589508026407318374">"फोन ऊष्ण होत आहे"</string>
     <string name="high_temp_notif_message" msgid="5642466103153429279">"फोन थंड होत असताना काही वैशिष्‍ट्ये मर्यादित असतात"</string>
-    <string name="high_temp_dialog_message" msgid="6840700639374113553">"आपला फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही आपला फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nआपला फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
+    <string name="high_temp_dialog_message" msgid="6840700639374113553">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
     <string name="lockscreen_shortcut_left" msgid="2182769107618938629">"डावा शॉर्टकट"</string>
     <string name="lockscreen_shortcut_right" msgid="3328683699505226536">"उजवा शॉर्टकट"</string>
     <string name="lockscreen_unlock_left" msgid="2043092136246951985">"डावा शॉर्टकट देखील अनलॉक करतो"</string>
diff --git a/packages/SystemUI/res/values-mr/strings_tv.xml b/packages/SystemUI/res/values-mr/strings_tv.xml
index aac2d5e..8ed1558 100644
--- a/packages/SystemUI/res/values-mr/strings_tv.xml
+++ b/packages/SystemUI/res/values-mr/strings_tv.xml
@@ -22,5 +22,5 @@
     <string name="notification_channel_tv_pip" msgid="134047986446577723">"चित्रा-मध्‍ये-चित्र"</string>
     <string name="pip_notification_unknown_title" msgid="6289156118095849438">"(शीर्षक नसलेला कार्यक्रम)"</string>
     <string name="pip_close" msgid="3480680679023423574">"PIP बंद करा"</string>
-    <string name="pip_fullscreen" msgid="8604643018538487816">"पूर्ण स्क्रीन"</string>
+    <string name="pip_fullscreen" msgid="8604643018538487816">"फुल स्क्रीन"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index e6f7abb..7a6a1e3 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Peranti tidak bernama"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Bersedia untuk menghantar"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Tiada peranti tersedia"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi tidak disambungkan"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Terbalikkan warna"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Dilog masuk sebagai <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Tiada Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Buka butiran."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Tidak tersedia disebabkan <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Buka tetapan <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edit susunan tetapan."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> daripada <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 1103b3c..62970aa 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"ဝန်ဆောင်မှုပေးသူ ကွန်ရက် ပြောင်းလဲနေသည်။"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"ဘက်ထရီ အသေးစိတ် အချက်အလက်များကို ဖွင့်ပါ"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"ဘတ္တရီ <xliff:g id="NUMBER">%d</xliff:g> ရာခိုင်နှုန်း။"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ဘက်ထရီအားသွင်းနေသည်၊ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ရာခိုင်နှုန်း။"</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ဘက်ထရီအားသွင်းနေသည်၊ <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%။"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"စနစ်အပြင်အဆင်များ"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"အကြောင်းကြားချက်များ။"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"သတိပေးချက်များအားလုံးကို ကြည့်ရန်"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"အမည်မတပ် ကိရိယာ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ကာစ်တ် လုပ်ရန် အသင့် ရှိနေပြီ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ကိရိယာများ မရှိ"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi ချိတ်ဆက်ထားခြင်းမရှိပါ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"အလင်းတောက်ပမှု"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"အလိုအလျောက်"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"အရောင်များ ပြောင်းပြန်လုပ်ရန်"</string>
@@ -605,7 +606,7 @@
     <string name="do_not_silence_block" msgid="4070647971382232311">"အသံ မတိတ်ပါနှင့် သို့မဟုတ် မပိတ်ဆို့ပါနှင့်"</string>
     <string name="tuner_full_importance_settings" msgid="3207312268609236827">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများ"</string>
     <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ဖွင့်ပါ"</string>
-    <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ပိတ်ပါ"</string>
+    <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ပိတ်ထားသည်"</string>
     <string name="power_notification_controls_description" msgid="4372459941671353358">"ပါဝါအကြောင်းကြားချက် ထိန်းချုပ်မှုများကိုအသုံးပြုပြီး အက်ပ်တစ်ခု၏ အကြောင်းကြားချက် အရေးပါမှု ၀ မှ ၅ အထိသတ်မှတ်ပေးနိုင်သည်။ \n\n"<b>"အဆင့် ၅"</b>" \n- အကြောင်းကြားချက်စာရင်း၏ ထိပ်ဆုံးတွင် ပြသည် \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်းကို ခွင့်ပြုသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၄"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- အမြဲတမ်း ခေတ္တပြပါမည် \n\n"<b>"အဆင့် ၃"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n\n"<b>"အဆင့် ၂"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n\n"<b>"အဆင့် ၁"</b>" \n- မျက်နှာပြင်အပြည့် ကြားဖြတ်ဖော်ပြခြင်း မရှိစေရန် ကာကွယ်ပေးသည် \n- ဘယ်တော့မှ ခေတ္တပြခြင်း မရှိပါ \n- အသံမြည်ခြင်းနှင့် တုန်ခါခြင်းများ ဘယ်တော့မှ မပြုလုပ်ပါ \n- လော့ခ်ချထားသည့် မျက်နှာပြင်နှင့် အခြေအနေဘားတန်းတို့တွင် မပြပါ \n- အကြောင်းကြားချက်စာရင်း အောက်ဆုံးတွင်ပြသည် \n\n"<b>"အဆင့် ၀"</b>" \n- အက်ပ်မှ အကြောင်းကြားချက်များ အားလုံးကို ပိတ်ဆို့သည်"</string>
     <string name="notification_header_default_channel" msgid="7506845022070889909">"အကြောင်းကြားချက်များ"</string>
     <string name="notification_channel_disabled" msgid="344536703863700565">"ဤအကြောင်းကြားချက်များကို မြင်ရတော့မည် မဟုတ်ပါ"</string>
@@ -707,7 +708,7 @@
     <string name="accessibility_data_saver_on" msgid="8454111686783887148">"ဒေတာချွေတာမှု ဖွင့်ထားသည်"</string>
     <string name="accessibility_data_saver_off" msgid="8841582529453005337">"ဒေတာချွေတာမှု ပိတ်ထားသည်"</string>
     <string name="switch_bar_on" msgid="1142437840752794229">"ဖွင့်ပါ"</string>
-    <string name="switch_bar_off" msgid="8803270596930432874">"ပိတ်ပါ"</string>
+    <string name="switch_bar_off" msgid="8803270596930432874">"ပိတ်ထားသည်"</string>
     <string name="nav_bar" msgid="1993221402773877607">"ရွှေ့လျားရန်ဘားတန်း"</string>
     <string name="nav_bar_layout" msgid="3664072994198772020">"အပြင်အဆင်"</string>
     <string name="left_nav_bar_button_type" msgid="8555981238887546528">"လက်ဝဲခလုတ် အမျိုးအစားအပို"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> အဖြစ် လက်မှတ်ထိုးဝင်ထားသည်။"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"အင်တာနက် မရှိပါ"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"အသေးစိတ်များကို ဖွင့်ပါ။"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> ကြောင့် မရနိုင်ပါ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ဆက်တင်များကို ဖွင့်ပါ။"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ဆက်တင်များ၏ အစီအစဉ်ကို တည်းဖြတ်ပါ။"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"စာမျက်နှာ <xliff:g id="ID_2">%2$d</xliff:g> အနက်မှ စာမျက်နှာ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5db7a3c..81ff8e2 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Enhet uten navn"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klar til å caste"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Ingen enheter er tilgjengelige"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi er ikke tilkoblet"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter farger"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Logget på som <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Ingen Internett-tilkobling"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Åpne informasjonen."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Utilgjengelig fordi <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Endre rekkefølgen på innstillingene."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Side <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e77c5cd..4f2845e 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"बेनाम उपकरण"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"प्रसारण गर्न तयार"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"कुनै उपकरणहरू उपलब्ध छैन"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi जडान गरिएको छैन"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"उज्यालपन"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वतः"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"रंग उल्टाउनुहोस्"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> को रूपमा साइन इन गरियो"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"इन्टरनेट छैन"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"विवरणहरूलाई खोल्नुहोस्।"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> का कारण उपलब्ध छैन"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> सम्बन्धी सेटिङहरूलाई खोल्नुहोस्।"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"सेटिङहरूको क्रमलाई सम्पादन गर्नुहोस्।"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> मध्ये पृष्ठ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 58d7949..60ca532 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Netwerk van provider wordt gewijzigd"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Accudetails openen"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batterij: <xliff:g id="NUMBER">%d</xliff:g> procent."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batterij wordt opgeladen, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> procent."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batterij wordt opgeladen, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%% procent."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Systeeminstellingen."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Meldingen."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Alle meldingen bekijken"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Naamloos apparaat"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klaar om te casten"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Geen apparaten beschikbaar"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wifi niet verbonden"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATISCH"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Kleuren omkeren"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Ingelogd als <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Geen internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Details openen."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Niet beschikbaar vanwege <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g>-instellingen openen."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Volgorde van instellingen bewerken."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 9f4cedf..a78f5cd 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ନାମହୀନ ଡିଭାଇସ୍‍"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"କାଷ୍ଟ୍ ପାଇଁ ପ୍ରସ୍ତୁତ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"କୌଣସି ଡିଭାଇସ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ୱାଇ-ଫାଇ ସଂଯୋଜିତ ହୋଇନାହିଁ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ଉଜ୍ଜ୍ୱଳତା"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ସ୍ୱଚାଳିତ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ରଙ୍ଗ ପୂର୍ବପରି କରନ୍ତୁ"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ଭାବରେ ସାଇନ୍‌ ଇନ୍‌ କରିଛନ୍ତି"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"କୌଣସି ଇଣ୍ଟରନେଟ୍‌ କନେକ୍ସନ୍ ନାହିଁ"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ବିବରଣୀ ଖୋଲନ୍ତୁ"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ସେଟିଙ୍ଗର କ୍ରମ ସଂଶୋଧନ କରନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ପୃଷ୍ଠା <xliff:g id="ID_1">%1$d</xliff:g> ମୋଟ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 5a1fff4..7e9a916 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ਬਿਨਾਂ ਨਾਮ ਦਾ ਡੀਵਾਈਸ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ਜੋੜਨ ਲਈ ਤਿਆਰ"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ਕੋਈ ਡਿਵਾਈਸਾਂ ਉਪਲਬਧ ਨਹੀਂ"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ਵਾਈ-ਫਾਈ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ਚਮਕ"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ਆਟੋ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"ਰੰਗ ਉਲਟੋ"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਉਹ ਸਭ ਕੁਝ ਕੈਪਚਰ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰ ਦੇਵੇਗਾ, ਜੋ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਤੇ ਡਿਸਪਲੇ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"ਦੁਬਾਰਾ ਨਾ ਦਿਖਾਓ"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"ਹੁਣ ਚਾਲੂ ਕਰੋ"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"ਕੋਈ ਸੂਚਨਾਵਾਂ ਨਹੀਂ"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ਵਜੋਂ ਸਾਈਨ ਇਨ ਕੀਤਾ"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ।"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ਵੇਰਵੇ ਖੋਲ੍ਹੋ।"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> ਦੇ ਕਾਰਨ ਅਣਉਪਲਬਧ ਹੈ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ਸੈਟਿੰਗਾਂ ਦੇ ਕ੍ਰਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ।"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> ਦਾ <xliff:g id="ID_1">%1$d</xliff:g> ਪੰਨਾ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 41bac38..22ef98c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -323,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Urządzenie bez nazwy"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gotowy do działania"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Brak dostępnych urządzeń"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Brak połączenia z Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jasność"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATYCZNA"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Odwróć kolory"</string>
@@ -794,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Jesteś zalogowany jako <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Brak internetu"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otwórz szczegóły."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Niedostępne z powodu: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otwórz ustawienia: <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Edytuj kolejność ustawień."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Strona <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4c0df134..f26d1ba 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Alteração de rede da operadora"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir detalhes da bateria"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Bateria carregando: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Configurações do sistema"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificações."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Ver todas as notificações"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto para transmitir"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Não há dispositivos disponíveis"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi não conectado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Login efetuado como <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Sem Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Abrir detalhes."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Indisponível devido a <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir configurações de <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar ordem das configurações."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 81be3f6..e6ed4dc 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto para transmitir"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Sem dispositivos disponíveis"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi não ligado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Sessão iniciada como <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Sem Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Abrir os detalhes."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Indisponível devido a <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir as definições de <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar a ordem das definições."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4c0df134..f26d1ba 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Alteração de rede da operadora"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir detalhes da bateria"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Bateria carregando: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Configurações do sistema"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificações."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Ver todas as notificações"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto para transmitir"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Não há dispositivos disponíveis"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi não conectado"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Login efetuado como <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Sem Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Abrir detalhes."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Indisponível devido a <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Abrir configurações de <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editar ordem das configurações."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index b5bd02d5..c553f6e 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -175,9 +175,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Se schimbă rețeaua operatorului"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Deschideți detaliile privind bateria"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterie: <xliff:g id="NUMBER">%d</xliff:g> la sută."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Se încarcă bateria, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Setări de sistem."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Notificări."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Vedeți toate notificările"</string>
@@ -323,6 +321,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispozitiv nedenumit"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pregătit pentru proiecție"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Niciun dispozitiv disponibil"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Rețeaua Wi-Fi nu este conectată"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminozitate"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAT"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversați culorile"</string>
@@ -790,6 +789,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Conectat(ă) ca <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Fără conexiune la internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Deschideți detaliile."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Indisponibile deoarece <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Deschideți setările <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Editați ordinea setărilor."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 23324a21..07aa66a 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -176,9 +176,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Сменить сеть"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Сведения о расходе заряда батареи"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд батареи в процентах: <xliff:g id="NUMBER">%d</xliff:g>."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Зарядка батареи. Текущий заряд: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Настройки"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Уведомления"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Показать все уведомления"</string>
@@ -325,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Безымянное устройство"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Готово к передаче"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Нет доступных устройств"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Нет подключения к сети Wi-Fi."</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркость"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОНАСТРОЙКА"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Обратные цвета"</string>
@@ -796,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Выполнен вход под именем <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Нет подключения к Интернету."</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Показать подробности."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Недоступно. <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Открыть настройки <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Изменить порядок быстрых настроек."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Страница <xliff:g id="ID_1">%1$d</xliff:g> из <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -868,14 +868,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Да"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Нет"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Нажмите, чтобы настроить режим энергосбережения"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_text (6324376061044218113) -->
-    <skip />
+    <string name="auto_saver_text" msgid="6324376061044218113">"Включать автоматически при заряде батареи ниже <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Отмена"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Автоматический переход в режим энергосбережения включен"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
-    <skip />
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Режим энергосбережения активируется при заряде батареи ниже <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Открыть настройки"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"ОК"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Передача SysUI"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 98b79be..aac62a2 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"වාහක ජාලය වෙනස් වෙමින්"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"බැටරි විස්තර විවෘත කරන්න"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"බැටරි ප්‍රතිශතය <xliff:g id="NUMBER">%d</xliff:g>"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"බැටරිය ආරෝපණය කරමින්, සියයට <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"බැටරිය ආරෝපණය කරමින්, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"පද්ධති සැකසීම්."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"දැනුම්දීම්."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"සියලු දැනුම්දීම් බලන්න"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"නම් නොකළ උපාංගය"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"කාස්ට් කිරීමට සුදානම්"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"උපාංග නොතිබේ"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi සම්බන්ධ නොවීය"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"දීප්තිමත් බව"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ස්වයංක්‍රීය"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"වර්ණ යටිකුරු කරන්න"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ලෙස පුරන්න"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"අන්තර්ජාලය නැත"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"විස්තර විවෘත කරන්න."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> හේතුවෙන් ලබා ගත නොහැකිය"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> සැකසීම් විවෘත කරන්න."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"සැකසීම්වල අනුපිළිවෙළ සංංස්කරණය කරන්න."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g> න් <xliff:g id="ID_1">%1$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index d871cb4..58e176a 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -176,9 +176,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Mení sa sieť operátora"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Otvoriť podrobnosti o batérii"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batéria <xliff:g id="NUMBER">%d</xliff:g> percent."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Nabíja sa batéria, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Nastavenia systému."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Upozornenia."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Zobraziť všetky upozornenia"</string>
@@ -325,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nepomenované zariadenie"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pripravené na prenášanie"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nie sú k dispozícii žiadne zariadenia"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Sieť Wi‑Fi nie je pripojená"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jas"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverzia farieb"</string>
@@ -796,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prihlásený používateľ <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Žiadny internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Otvoriť podrobnosti"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nedostupné z nasledujúceho dôvodu: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Otvoriť nastavenia <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Upraviť poradie nastavení"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Strana <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index e8f04af..1323fcd 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -176,9 +176,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Spreminjanje omrežja operaterja"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Odpiranje podrobnosti o akumulatorju"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterija <xliff:g id="NUMBER">%d</xliff:g> odstotkov."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Polnjenje akumulatorja, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Sistemske nastavitve."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Obvestila."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Prikaži vsa obvestila"</string>
@@ -325,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovana naprava"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pripravljeno za predvajanje"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Na voljo ni nobene naprave"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Povezava Wi-Fi ni vzpostavljena"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Svetlost"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"SAMODEJNO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Obrni barve"</string>
@@ -796,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Prijavljeni ste kot <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Brez internetne povezave"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Odpri podrobnosti."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Ni na voljo zaradi tega razloga: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Odpri nastavitve za <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Uredi vrstni red nastavitev."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. stran od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -868,14 +868,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Dovoli"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Zavrni"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Dotaknite se za načrtovanje varčevanja z energijo akumulatorja"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_text (6324376061044218113) -->
-    <skip />
+    <string name="auto_saver_text" msgid="6324376061044218113">"Samodejno vklopi, ko je energija akumulatorja na <xliff:g id="PERCENTAGE">%d</xliff:g> %%"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, hvala"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Urnik varčevanja z energijo akumulatorja je vklopljen"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
-    <skip />
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Varčevanje z energijo akumulatorja se bo samodejno vklopilo, ko bo energija akumulatorja pod <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Nastavitve"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"V redu"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Izvoz kopice SysUI"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 429e84d..8f08665 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Rrjeti i operatorit celular po ndryshohet"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Hap detajet e baterisë"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria ka edhe <xliff:g id="NUMBER">%d</xliff:g> për qind."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Bateria po ngarkohet, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> për qind."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Bateria po ngarkohet, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Cilësimet e sistemit."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Njoftimet."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Shiko të gjitha njoftimet"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Pajisje e paemërtuar"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gati për transmetim"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nuk ofrohet për përdorim asnjë pajisje"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi nuk është lidhur"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ndriçimi"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Automatike"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Shkëmbe ngjyrat"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Identifikuar si <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Nuk ka internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Hap detajet."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Nuk ofrohet për shkak se <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Hap cilësimet e <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Modifiko rendin e cilësimeve."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Faqja <xliff:g id="ID_1">%1$d</xliff:g> nga <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index a164876..609f332 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -175,7 +175,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Промена мреже мобилног оператера"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Отвори детаље о батерији"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Батерија је на <xliff:g id="NUMBER">%d</xliff:g> посто."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батерија се пуни, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> процената."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Батерија се пуни, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Системска подешавања."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Обавештења."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Погледајте сва обавештења"</string>
@@ -321,6 +321,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Неименовани уређај"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Спремно за пребацивање"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Није доступан ниједан уређај"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi није повезан"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Осветљеност"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АУТОМАТСКА"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Обрни боје"</string>
@@ -788,6 +789,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Пријављени сте као <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Нема интернета"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Отвори детаље."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Није доступно из следећег разлога: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Отвори подешавања за <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Измени редослед подешавања."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>. страна од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 37614d9..72ae99b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Namnlös enhet"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Redo att casta"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Inga tillgängliga enheter"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Inte ansluten till Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertera färger"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Inloggad som <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Inget internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Visa information."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Inte tillgänglig på grund av <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Öppna <xliff:g id="ID_1">%s</xliff:g>-inställningarna."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Ändra ordning på inställningarna."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Sida <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 5cbd2db..a73d0e7 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Kifaa hakina jina"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tayari kutuma"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Hakuna vifaa vilivyopatikana"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi haijaunganishwa"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ung\'avu"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"KIOTOMATIKI"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Pindua rangi"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Umeingia katika akaunti ya <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Hakuna intaneti"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Fungua maelezo."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Haipatikani kutokana na <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Fungua mipangilio ya <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Badilisha orodha ya mipangilio."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Ukurasa wa <xliff:g id="ID_1">%1$d</xliff:g> kati ya <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 1f498e6..02457d9 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"பெயரிடப்படாத சாதனம்"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"திரையிடத் தயார்"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"சாதனங்கள் இல்லை"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"வைஃபை இணைக்கப்படவில்லை"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ஒளிர்வு"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"தானியங்கு"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"வண்ணங்களை மாற்று"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"திரையில் காட்டப்படும் அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> படமெடுக்கும்."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"மீண்டும் காட்டாதே"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"எல்லாவற்றையும் அழி"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"அறிவிப்புகளை நிர்வகி"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தின் மூலம் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"இப்போது தொடங்கு"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"அறிவிப்புகள் இல்லை"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> என்ற பெயரில் உள்நுழைந்துள்ளீர்கள்"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"இணைய இணைப்பு இல்லை"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"விவரங்களைத் திற."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> என்பதால் தற்போது முடக்கப்பட்டுள்ளது"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> அமைப்புகளைத் திற."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"அமைப்புகளின் வரிசை முறையைத் திருத்து."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"பக்கம் <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 6c8f728d3..a6d7699 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"క్యారియర్ నెట్‌వర్క్ మారుతోంది"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"బ్యాటరీ వివరాలను తెరుస్తుంది"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"బ్యాటరీ <xliff:g id="NUMBER">%d</xliff:g> శాతం."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"బ్యాటరీ ఛార్జ్ అవుతోంది, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> శాతం."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"బ్యాటరీ ఛార్జ్ అవుతోంది, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> %%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"సిస్టమ్ సెట్టింగ్‌లు."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"నోటిఫికేషన్‌లు."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"అన్ని నోటిఫికేషన్‌లను చూడండి"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"పేరులేని పరికరం"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ప్రసారం చేయడానికి సిద్ధంగా ఉంది"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"పరికరాలు ఏవీ అందుబాటులో లేవు"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi కనెక్ట్ కాలేదు"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ప్రకాశం"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"స్వయంచాలకం"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"రంగులను తారుమారు చేయి"</string>
@@ -442,8 +443,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> మీ స్క్రీన్‌పై కనిపించే ప్రతిదాన్ని క్యాప్చర్ చేయడం ప్రారంభిస్తుంది."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"మళ్లీ చూపవద్దు"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"అన్నీ క్లియర్ చేయండి"</string>
-    <!-- no translation found for manage_notifications_text (2386728145475108753) -->
-    <skip />
+    <string name="manage_notifications_text" msgid="2386728145475108753">"నిర్వహించండి"</string>
     <string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"అంతరాయం కలిగించవద్దు ద్వారా నోటిఫికేషన్‌లు పాజ్ చేయబడ్డాయి"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"ఇప్పుడే ప్రారంభించు"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"నోటిఫికేషన్‌లు లేవు"</string>
@@ -783,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేసారు"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ఇంటర్నెట్ లేదు"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"వివరాలను తెరవండి."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> కారణంగా అందుబాటులో లేదు"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> సెట్టింగ్‌లను తెరవండి."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"సెట్టింగ్‌ల క్రమాన్ని సవరించండి."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d72d2d6..8c78687 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"อุปกรณ์ที่ไม่มีชื่อ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"พร้อมที่จะส่ง"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"ไม่มีอุปกรณ์ที่สามารถใช้ได้"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"ไม่ได้เชื่อมต่อ Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ความสว่าง"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"อัตโนมัติ"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"สลับสี"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"ลงชื่อเข้าใช้เป็น <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"ไม่มีอินเทอร์เน็ต"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"เปิดรายละเอียด"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"ไม่พร้อมใช้งานเนื่องจาก<xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"เปิดการตั้งค่า <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"แก้ไขลำดับการตั้งค่า"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"หน้า <xliff:g id="ID_1">%1$d</xliff:g> จาก <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index c67aca4..21cf1f2 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Walang pangalang device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Handang mag-cast"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Walang available na mga device"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Hindi nakakonekta sa Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"I-invert ang mga kulay"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Naka-sign in bilang <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Walang internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Buksan ang mga detalye."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Hindi available dahil sa <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Buksan ang mga setting ng <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"I-edit ang pagkakasunud-sunod ng mga setting."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Page <xliff:g id="ID_1">%1$d</xliff:g> ng <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index edb9262..fb200ec 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Adsız cihaz"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Yayın için hazır"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Kullanılabilir cihaz yok"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Kablosuz ağ bağlı değil"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Parlaklık"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATİK"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Renkleri çevir"</string>
@@ -372,7 +373,7 @@
     <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Ekranı sola doğru böl"</string>
     <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Ekranı sağa doğru böl"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Genel bakışı aç/kapat"</string>
-    <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ödeme alındı"</string>
+    <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Şarj oldu"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Şarj oluyor"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Tam şarj olmasına <xliff:g id="CHARGING_TIME">%s</xliff:g> kaldı"</string>
     <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Şarj olmuyor"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> olarak oturum açıldı"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"İnternet yok"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Ayrıntıları aç."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> nedeniyle kullanılamıyor"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ayarlarını aç."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Ayarların sırasını düzenle."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Sayfa <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 3d82520..82b56a2 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -176,9 +176,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Змінення мережі оператора"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Відкрити деталі акумулятора"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд акумулятора у відсотках: <xliff:g id="NUMBER">%d</xliff:g>."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Акумулятор заряджається: <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Налаштування системи."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Сповіщення."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Переглянути всі сповіщення"</string>
@@ -325,6 +323,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Пристрій без назви"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Готово до трансляції"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Немає пристроїв"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi не під’єднано"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яскравість"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТО"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Інвертувати кольори"</string>
@@ -796,6 +795,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Ви ввійшли як <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Немає Інтернету"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Відкрити деталі."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Недоступно, оскільки <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Відкрити налаштування <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Змінити порядок налаштувань."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Сторінка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
@@ -868,10 +868,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Дозволити"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Відмовити"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Торкніться, щоб увімкнути автоматичний режим економії заряду акумулятора"</string>
-    <string name="auto_saver_text" msgid="6324376061044218113">"Вмикати автоматично, коли рівень знижується до <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string>
+    <string name="auto_saver_text" msgid="6324376061044218113">"Вмикати автоматично, коли заряд акумулятора знижується до <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Ні, дякую"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Автоматичний режим економії заряду акумулятора ввімкнено"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Режим економії заряду акумулятора вмикатиметься автоматично, коли рівень нижчий за <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Режим економії заряду акумулятора вмикається автоматично, коли рівень заряду нижчий за <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Налаштування"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 6b9c737..b02ceae 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"کیریئر نیٹ ورک کی تبدیلی"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"بیٹری کی تفصیلات کھولیں"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"بیٹری <xliff:g id="NUMBER">%d</xliff:g> فیصد۔"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"بیٹری چارجنگ، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> فیصد۔"</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"بیٹری چارج ہو رہی ہے، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"سسٹم کی ترتیبات۔"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"اطلاعات۔"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"تمام اطلاعات دیکھیں"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"بغیر نام والا آلہ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"کاسٹ کرنے کیلئے تیار"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"کوئی آلات دستیاب نہیں ہیں"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"‏Wi-Fi سے منسلک نہیں ہے"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"چمکیلا پن"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"خودکار"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"رنگ پلٹیں"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> کے بطور سائن ان ہے"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"انٹرنیٹ نہیں ہے"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"تفصیلات کھولیں۔"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g> کی وجہ سے دستیاب نہیں ہے"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ترتیبات کھولیں۔"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ترتیبات کی ترتیب میں ترمیم کریں۔"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"صفحہ <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index e2b5df4..a128e7f 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -127,7 +127,7 @@
     <string name="accessibility_data_one_bar" msgid="1415625833238273628">"Ma’lumotlar bitta panelda."</string>
     <string name="accessibility_data_two_bars" msgid="6166018492360432091">"Ma’lumotlar ikkita panelda."</string>
     <string name="accessibility_data_three_bars" msgid="9167670452395038520">"Ma’lumotlar uchta panelda."</string>
-    <string name="accessibility_data_signal_full" msgid="2708384608124519369">"Ma’lumot uzatish signali to‘liq."</string>
+    <string name="accessibility_data_signal_full" msgid="2708384608124519369">"Internet signali butun."</string>
     <string name="accessibility_wifi_name" msgid="7202151365171148501">"Ulangan: <xliff:g id="WIFI">%s</xliff:g>."</string>
     <string name="accessibility_bluetooth_name" msgid="8441517146585531676">"Ulangan: <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
     <string name="accessibility_cast_name" msgid="4026393061247081201">"Bunga ulangan: <xliff:g id="CAST">%s</xliff:g>."</string>
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Mobil tarmoqni o‘zgartirish"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Batareya quvvati sarfi haqida ma’lumot"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"Batareya <xliff:g id="NUMBER">%d</xliff:g> foiz."</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batareya quvvat olmoqda (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%)."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Tizim sozlamalari."</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Eslatmalar."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Barcha bildirishnomalarni ko‘rish"</string>
@@ -300,7 +298,7 @@
     <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Eniga"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"Kiritish usuli"</string>
     <string name="quick_settings_location_label" msgid="5011327048748762257">"Joylashuv"</string>
-    <string name="quick_settings_location_off_label" msgid="7464544086507331459">"Joylashuv xizmati o‘chiq"</string>
+    <string name="quick_settings_location_off_label" msgid="7464544086507331459">"Joylashuvni aniqlash xizmati yoqilmagan"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"Media qurilma"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
     <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Favqulodda chaqiruvlar"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nomsiz qurilma"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tarqatish uchun tayyor"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Qurilmalar topilmadi"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi-Fi tarmoqqa ulanmagan"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Yorqinlik"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AVTOMATIC"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Teskari ranglar"</string>
@@ -782,8 +781,9 @@
     <string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"Tezkor sozlamalarni yopish."</string>
     <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"Signal o‘rnatildi."</string>
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> sifatida kirgansiz"</string>
-    <string name="data_connection_no_internet" msgid="4503302451650972989">"Internetga ulanmagan"</string>
+    <string name="data_connection_no_internet" msgid="4503302451650972989">"Internetga ulanmagansiz"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Tafsilotlarini ko‘rsatish."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Mavjud emas, sababi: <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> sozlamalarini ochish."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Sozlamalar tartibini o‘zgartirish."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"<xliff:g id="ID_1">%1$d</xliff:g>-sahifa, jami: <xliff:g id="ID_2">%2$d</xliff:g> ta sahifa"</string>
@@ -856,10 +856,10 @@
     <string name="slice_permission_allow" msgid="2340244901366722709">"Ruxsat"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Rad etish"</string>
     <string name="auto_saver_title" msgid="1217959994732964228">"Quvvat tejash rejimini rejalashtirish uchun bosing"</string>
-    <string name="auto_saver_text" msgid="6324376061044218113">"Batareya quvvati <xliff:g id="PERCENTAGE">%d</xliff:g>%% ga tushganda avtomatik yoqish"</string>
+    <string name="auto_saver_text" msgid="6324376061044218113">"Batareya quvvati <xliff:g id="PERCENTAGE">%d</xliff:g>%% ga tushganida avtomatik yoqish"</string>
     <string name="no_auto_saver_action" msgid="8086002101711328500">"Kerak emas"</string>
     <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Quvvat tejash rejimi jadvali faollashtirildi"</string>
-    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Batareya quvvati <xliff:g id="PERCENTAGE">%d</xliff:g>%% ga tushganda, quvvat tejash rejimi avtomatik ravishda yoqiladi."</string>
+    <string name="auto_saver_enabled_text" msgid="874711029884777579">"Batareya quvvati <xliff:g id="PERCENTAGE">%d</xliff:g>%% ga tushsa, quvvat tejash rejimi avtomatik ravishda yoqiladi."</string>
     <string name="open_saver_setting_action" msgid="8314624730997322529">"Sozlamalar"</string>
     <string name="auto_saver_okay_action" msgid="2701221740227683650">"OK"</string>
     <string name="heap_dump_tile_name" msgid="9141031328971226374">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8f98557..c7b9178 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"Thay đổi mạng của nhà mạng"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"Mở chi tiết về pin"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> phần trăm pin."</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Đang sạc pin, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> phần trăm."</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Đang sạc pin, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"Cài đặt hệ thống"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"Thông báo."</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"Xem tất cả thông báo"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Thiết bị không có tên"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Sẵn sàng truyền"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Không có thiết bị nào"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Chưa kết nối với Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Độ sáng"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"TỰ ĐỘNG"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Đảo ngược màu"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Đã đăng nhập là <xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Không có Internet"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Mở chi tiết."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Không sử dụng được do <xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Mở cài đặt <xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Chỉnh sửa thứ tự cài đặt."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Trang <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 27cad95..0bfcdcf 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"运营商网络正在更改"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"打开电量详情"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"电池电量为百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"正在充电,已完成百分之<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>。"</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"正在充电,已完成 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"系统设置。"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"查看所有通知"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名设备"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"已准备好投射"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"没有可用设备"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"未连接到 WLAN 网络"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自动"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"反色"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"目前登录的用户名为<xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"未连接到互联网"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"打开详情页面。"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>,因此目前无法使用"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"打开<xliff:g id="ID_1">%s</xliff:g>设置。"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"修改设置顺序。"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"第 <xliff:g id="ID_1">%1$d</xliff:g> 页,共 <xliff:g id="ID_2">%2$d</xliff:g> 页"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index da768b6..cf93802 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -174,9 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"流動網絡供應商網絡正在變更"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"開啟電池詳細資料"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"電池電量為百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"正在充電:<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"系統設定"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"睇所有通知"</string>
@@ -321,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名的裝置"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"放送準備完成"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"沒有可用裝置"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"未連線至 Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"反轉顏色"</string>
@@ -784,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"已登入為<xliff:g id="ID_1">%s</xliff:g>。"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"沒有互聯網連線"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"開啟詳細資料頁面。"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>,所以宜家用唔到"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"開啟<xliff:g id="ID_1">%s</xliff:g>設定頁面。"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"編輯設定次序。"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁 (共 <xliff:g id="ID_2">%2$d</xliff:g> 頁)"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 116f98b..c5a2f90 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -174,7 +174,7 @@
     <string name="carrier_network_change_mode" msgid="8149202439957837762">"電信業者網路正在進行變更"</string>
     <string name="accessibility_battery_details" msgid="7645516654955025422">"開啟電量詳細資料"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"電池電量為百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
-    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"充電中,已完成百分之 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>。"</string>
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"充電中,已完成 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"系統設定"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"通知。"</string>
     <string name="accessibility_overflow_action" msgid="5681882033274783311">"查看所有通知"</string>
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名的裝置"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"可以開始投放了"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"沒有可用裝置"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"未連線至 Wi-Fi"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"反轉顏色"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"以「<xliff:g id="ID_1">%s</xliff:g>」的身分登入"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"沒有網際網路連線"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"開啟詳細資料。"</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"<xliff:g id="REASON">%s</xliff:g>,因此目前無法使用"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"開啟「<xliff:g id="ID_1">%s</xliff:g>」設定。"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"編輯設定順序。"</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁,共 <xliff:g id="ID_2">%2$d</xliff:g> 頁"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 53d3b9c..2fc207a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -319,6 +319,7 @@
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Idivayisi engenalo igama"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ilungele ukusakaza"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Ayikho idivayisi etholakalayo"</string>
+    <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"I-Wi-Fi ayixhunyiwe"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ukugqama"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OKUZENZAKALELAYO"</string>
     <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Faka imibala"</string>
@@ -782,6 +783,7 @@
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"Ungene ngemvume njengo-<xliff:g id="ID_1">%s</xliff:g>"</string>
     <string name="data_connection_no_internet" msgid="4503302451650972989">"Ayikho i-inthanethi"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"Vula imininingwane."</string>
+    <string name="accessibility_quick_settings_not_available" msgid="4190068184294019846">"Ayitholakali ngenxa ye-<xliff:g id="REASON">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"Vula izilungiselelo ze-<xliff:g id="ID_1">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"Hlela uhlelo lwezilungiselelo."</string>
     <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Ikhasi <xliff:g id="ID_1">%1$d</xliff:g> kwangu-<xliff:g id="ID_2">%2$d</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2fbf42f..6378309 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -137,15 +137,9 @@
     <integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
     <integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
 
-    <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
-    <bool name="config_show4GForLTE">true</bool>
-
     <!-- Show indicator for Wifi on but not connected. -->
     <bool name="config_showWifiIndicatorWhenEnabled">false</bool>
 
-    <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
-    <bool name="config_hideLtePlus">false</bool>
-
     <!-- The number of milliseconds before the heads up notification auto-dismisses. -->
     <integer name="heads_up_notification_decay">5000</integer>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
index 19e8673..114d69e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
@@ -27,6 +27,7 @@
 /**
  * Background task resource loader
  */
+@Deprecated
 class BackgroundTaskLoader implements Runnable {
     static String TAG = "BackgroundTaskLoader";
     static boolean DEBUG = false;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
index 898d64a..7e0f8fe 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/FilteredTaskList.java
@@ -27,6 +27,7 @@
 /**
  * A list of filtered tasks.
  */
+@Deprecated
 class FilteredTaskList {
 
     private final ArrayList<Task> mTasks = new ArrayList<>();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
index 852463f..f02bc5a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/HighResThumbnailLoader.java
@@ -34,6 +34,7 @@
 /**
  * Loader class that loads full-resolution thumbnails when appropriate.
  */
+@Deprecated
 public class HighResThumbnailLoader implements
         TaskCallbacks, BackgroundTaskLoader.OnIdleChangedListener {
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
index f69e911..8b3ae42 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
@@ -45,6 +45,7 @@
  *   3) executePlan() will actually load and fill in the icons and thumbnails according to the load
  *      options specified, such that we can transition into the Recents activity seamlessly
  */
+@Deprecated
 public class RecentsTaskLoadPlan {
 
     /** The set of conditions to preload tasks. */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
index 996c837..b50aa76 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
@@ -42,6 +42,7 @@
 /**
  * Recents task loader
  */
+@Deprecated
 public class RecentsTaskLoader {
     private static final String TAG = "RecentsTaskLoader";
     private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index b51004b..368e503 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.recents.model;
 
+import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -31,13 +32,14 @@
 import java.util.Objects;
 
 /**
- * A task represents the top most task in the system's task stack.
+ * A task in the recent tasks list.
  */
 public class Task {
 
     public static final String TAG = "Task";
 
     /* Task callbacks */
+    @Deprecated
     public interface TaskCallbacks {
         /* Notifies when a task has been bound */
         void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
@@ -65,6 +67,21 @@
 
         private int mHashCode;
 
+        public TaskKey(ActivityManager.RecentTaskInfo t) {
+            ComponentName sourceComponent = t.origActivity != null
+                    // Activity alias if there is one
+                    ? t.origActivity
+                    // The real activity if there is no alias (or the target if there is one)
+                    : t.realActivity;
+            this.id = t.taskId;
+            this.windowingMode = t.configuration.windowConfiguration.getWindowingMode();
+            this.baseIntent = t.baseIntent;
+            this.sourceComponent = sourceComponent;
+            this.userId = t.userId;
+            this.lastActiveTime = t.lastActiveTime;
+            updateHashCode();
+        }
+
         public TaskKey(int id, int windowingMode, Intent intent,
                 ComponentName sourceComponent, int userId, long lastActiveTime) {
             this.id = id;
@@ -125,7 +142,8 @@
     /**
      * The temporary sort index in the stack, used when ordering the stack.
      */
-    public int temporarySortIndexInStack;
+    @Deprecated
+    int temporarySortIndexInStack;
 
     /**
      * The icon is the task description icon (if provided), which falls back to the activity icon,
@@ -134,6 +152,7 @@
     public Drawable icon;
     public ThumbnailData thumbnail;
     @ViewDebug.ExportedProperty(category="recents")
+    @Deprecated
     public String title;
     @ViewDebug.ExportedProperty(category="recents")
     public String titleDescription;
@@ -142,6 +161,7 @@
     @ViewDebug.ExportedProperty(category="recents")
     public int colorBackground;
     @ViewDebug.ExportedProperty(category="recents")
+    @Deprecated
     public boolean useLightOnPrimaryColor;
 
     /**
@@ -153,10 +173,13 @@
      * The state isLaunchTarget will be set for the correct task upon launching Recents.
      */
     @ViewDebug.ExportedProperty(category="recents")
+    @Deprecated
     public boolean isLaunchTarget;
     @ViewDebug.ExportedProperty(category="recents")
+    @Deprecated
     public boolean isStackTask;
     @ViewDebug.ExportedProperty(category="recents")
+    @Deprecated
     public boolean isSystemApp;
     @ViewDebug.ExportedProperty(category="recents")
     public boolean isDockable;
@@ -165,6 +188,7 @@
      * Resize mode. See {@link ActivityInfo#resizeMode}.
      */
     @ViewDebug.ExportedProperty(category="recents")
+    @Deprecated
     public int resizeMode;
 
     @ViewDebug.ExportedProperty(category="recents")
@@ -173,12 +197,31 @@
     @ViewDebug.ExportedProperty(category="recents")
     public boolean isLocked;
 
+    @Deprecated
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
     public Task() {
         // Do nothing
     }
 
+    public Task(TaskKey key) {
+        this.key = key;
+        this.taskDescription = new TaskDescription();
+    }
+
+    public Task(TaskKey key, int colorPrimary, int colorBackground,
+            boolean isDockable, boolean isLocked, TaskDescription taskDescription,
+            ComponentName topActivity) {
+        this.key = key;
+        this.colorPrimary = colorPrimary;
+        this.colorBackground = colorBackground;
+        this.taskDescription = taskDescription;
+        this.isDockable = isDockable;
+        this.isLocked = isLocked;
+        this.topActivity = topActivity;
+    }
+
+    @Deprecated
     public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title,
             String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget,
             boolean isStackTask, boolean isSystemApp, boolean isDockable,
@@ -206,6 +249,7 @@
     /**
      * Copies the metadata from another task, but retains the current callbacks.
      */
+    @Deprecated
     public void copyFrom(Task o) {
         this.key = o.key;
         this.icon = o.icon;
@@ -228,6 +272,7 @@
     /**
      * Add a callback.
      */
+    @Deprecated
     public void addCallback(TaskCallbacks cb) {
         if (!mCallbacks.contains(cb)) {
             mCallbacks.add(cb);
@@ -237,11 +282,13 @@
     /**
      * Remove a callback.
      */
+    @Deprecated
     public void removeCallback(TaskCallbacks cb) {
         mCallbacks.remove(cb);
     }
 
     /** Updates the task's windowing mode. */
+    @Deprecated
     public void setWindowingMode(int windowingMode) {
         key.setWindowingMode(windowingMode);
         int callbackCount = mCallbacks.size();
@@ -251,6 +298,7 @@
     }
 
     /** Notifies the callback listeners that this task has been loaded */
+    @Deprecated
     public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) {
         this.icon = applicationIcon;
         this.thumbnail = thumbnailData;
@@ -261,6 +309,7 @@
     }
 
     /** Notifies the callback listeners that this task has been unloaded */
+    @Deprecated
     public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) {
         icon = defaultApplicationIcon;
         thumbnail = null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
index 5f3dcd1..d378189 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskFilter.java
@@ -21,6 +21,7 @@
 /**
  * An interface for a task filter to query whether a particular task should show in a stack.
  */
+@Deprecated
 public interface TaskFilter {
     /** Returns whether the filter accepts the specified task */
     boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
index 23582d4..342cb75 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyCache.java
@@ -34,7 +34,7 @@
      * Gets a specific entry in the cache with the specified key, regardless of whether the cached
      * value is valid or not.
      */
-    final synchronized V get(TaskKey key) {
+    public final synchronized V get(TaskKey key) {
         return getCacheEntry(key.id);
     }
 
@@ -42,7 +42,7 @@
      * Returns the value only if the key is valid (has not been updated since the last time it was
      * in the cache)
      */
-    final synchronized V getAndInvalidateIfModified(TaskKey key) {
+    public final synchronized V getAndInvalidateIfModified(TaskKey key) {
         TaskKey lastKey = mKeys.get(key.id);
         if (lastKey != null) {
             if ((lastKey.windowingMode != key.windowingMode) ||
@@ -59,7 +59,7 @@
     }
 
     /** Puts an entry in the cache for a specific key. */
-    final synchronized void put(TaskKey key, V value) {
+    public final synchronized void put(TaskKey key, V value) {
         if (key == null || value == null) {
             Log.e(TAG, "Unexpected null key or value: " + key + ", " + value);
             return;
@@ -70,14 +70,14 @@
 
 
     /** Removes a cache entry for a specific key. */
-    final synchronized void remove(TaskKey key) {
+    public final synchronized void remove(TaskKey key) {
         // Remove the key after the cache value because we need it to make the callback
         removeCacheEntry(key.id);
         mKeys.remove(key.id);
     }
 
     /** Removes all the entries in the cache. */
-    final synchronized void evictAll() {
+    public final synchronized void evictAll() {
         evictAllCache();
         mKeys.clear();
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
index fbb6ace..6b9b9f5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskResourceLoadQueue.java
@@ -21,6 +21,7 @@
 /**
  * A Task load queue
  */
+@Deprecated
 class TaskResourceLoadQueue {
 
     private final ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<>();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
index c731ac9..fd92bca 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskStack.java
@@ -33,6 +33,7 @@
 /**
  * The task stack contains a list of multiple tasks.
  */
+@Deprecated
 public class TaskStack {
 
     private static final String TAG = "TaskStack";
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
index 2de7f74..26f6b59 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/AnimationProps.java
@@ -34,6 +34,7 @@
  * The generic set of animation properties to animate a {@link View}. The animation can have
  * different interpolators, start delays and durations for each of the different properties.
  */
+@Deprecated
 public class AnimationProps {
 
     private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
index 45728c4..30bea32 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/view/AnimateableViewBounds.java
@@ -27,6 +27,7 @@
 /**
  * An outline provider that has a clip and outline that can be animated.
  */
+@Deprecated
 public class AnimateableViewBounds extends ViewOutlineProvider {
 
     private static final float MIN_ALPHA = 0.1f;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java
new file mode 100644
index 0000000..c42e7e3
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/KeyguardManagerCompat.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+
+public class KeyguardManagerCompat {
+    private final KeyguardManager mKeyguardManager;
+
+    public KeyguardManagerCompat(Context context) {
+        mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+    }
+
+    public boolean isDeviceLocked(int userId) {
+        return mKeyguardManager.isDeviceLocked(userId);
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java
new file mode 100644
index 0000000..a529903
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+
+public class RecentTaskInfoCompat {
+
+    private ActivityManager.RecentTaskInfo mInfo;
+
+    public RecentTaskInfoCompat(ActivityManager.RecentTaskInfo info) {
+        mInfo = info;
+    }
+
+    public int getUserId() {
+        return mInfo.userId;
+    }
+
+    public boolean supportsSplitScreenMultiWindow() {
+        return mInfo.supportsSplitScreenMultiWindow;
+    }
+
+    public ComponentName getTopActivity() {
+        return mInfo.topActivity;
+    }
+
+    public ActivityManager.TaskDescription getTaskDescription() {
+        return mInfo.taskDescription;
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
index 5a28a5e..7c8c23e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RotationWatcher.java
@@ -48,7 +48,7 @@
         if (!mIsWatching) {
             try {
                 WindowManagerGlobal.getWindowManagerService().watchRotation(mWatcher,
-                        mContext.getDisplay().getDisplayId());
+                        mContext.getDisplayId());
                 mIsWatching = true;
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to set rotation watcher", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
new file mode 100644
index 0000000..eaf8d9b
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskDescriptionCompat.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shared.system;
+
+import android.app.ActivityManager;
+
+public class TaskDescriptionCompat {
+
+    private ActivityManager.TaskDescription mTaskDescription;
+
+    public TaskDescriptionCompat(ActivityManager.TaskDescription td) {
+        mTaskDescription = td;
+    }
+
+    public int getPrimaryColor() {
+        return mTaskDescription != null
+                ? mTaskDescription.getPrimaryColor()
+                : 0;
+    }
+
+    public int getBackgroundColor() {
+        return mTaskDescription != null
+                ? mTaskDescription.getBackgroundColor()
+                : 0;
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
index b2b140e..de2a3e4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
@@ -17,7 +17,7 @@
 
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.view.DisplayListCanvas;
+import android.graphics.RecordingCanvas;
 import android.view.View;
 import android.view.ViewRootImpl;
 import android.view.WindowCallbacks;
@@ -55,7 +55,7 @@
         }
 
         @Override
-        public void onPostDraw(DisplayListCanvas canvas) {
+        public void onPostDraw(RecordingCanvas canvas) {
             WindowCallbacksCompat.this.onPostDraw(canvas);
         }
     };
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 265a961..34df15f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -29,13 +29,13 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
 import java.io.File;
 
@@ -50,13 +50,6 @@
  */
 public class KeyguardHostView extends FrameLayout implements SecurityCallback {
 
-    public interface OnDismissAction {
-        /**
-         * @return true if the dismiss should be deferred
-         */
-        boolean onDismiss();
-    }
-
     private AudioManager mAudioManager;
     private TelephonyManager mTelephonyManager = null;
     protected ViewMediatorCallback mViewMediatorCallback;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index a3862eb..a543d17 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -102,7 +102,8 @@
                         findViewById(R.id.key9)
                 },
                 new View[]{
-                        null, findViewById(R.id.key0), findViewById(R.id.key_enter)
+                        findViewById(R.id.delete_button), findViewById(R.id.key0),
+                        findViewById(R.id.key_enter)
                 },
                 new View[]{
                         null, mEcaView, null
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 6d1313c..db78667 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -28,7 +28,6 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import androidx.core.graphics.ColorUtils;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.util.ArraySet;
@@ -41,6 +40,8 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.core.graphics.ColorUtils;
+
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.ViewClippingUtil;
 import com.android.systemui.Dependency;
@@ -64,7 +65,6 @@
 
     private TextView mLogoutView;
     private KeyguardClockSwitch mClockView;
-    private View mClockSeparator;
     private TextView mOwnerInfo;
     private KeyguardSliceView mKeyguardSlice;
     private Runnable mPendingMarqueeStart;
@@ -75,8 +75,8 @@
     private boolean mWasPulsing;
     private float mDarkAmount = 0;
     private int mTextColor;
-    private float mWidgetPadding;
     private int mLastLayoutHeight;
+    private int mSmallClockPadding;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -175,14 +175,12 @@
         }
         mOwnerInfo = findViewById(R.id.owner_info);
         mKeyguardSlice = findViewById(R.id.keyguard_status_area);
-        mClockSeparator = findViewById(R.id.clock_separator);
         mVisibleInDoze = Sets.newArraySet(mClockView, mKeyguardSlice);
         mTextColor = mClockView.getCurrentTextColor();
 
         int clockStroke = getResources().getDimensionPixelSize(R.dimen.widget_small_font_stroke);
         mClockView.getPaint().setStrokeWidth(clockStroke);
         mClockView.addOnLayoutChangeListener(this);
-        mClockSeparator.addOnLayoutChangeListener(this);
         mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
         onSliceContentChanged();
 
@@ -199,26 +197,18 @@
     }
 
     /**
-     * Moves clock and separator, adjusting margins when slice content changes.
+     * Moves clock, adjusting margins when slice content changes.
      */
     private void onSliceContentChanged() {
         boolean smallClock = mKeyguardSlice.hasHeader() || mPulsing;
-        float clockScale = smallClock ? mSmallClockScale : 1;
-
         RelativeLayout.LayoutParams layoutParams =
                 (RelativeLayout.LayoutParams) mClockView.getLayoutParams();
-        int height = mClockView.getHeight();
-        layoutParams.bottomMargin = (int) -(height - (clockScale * height));
+        layoutParams.bottomMargin = smallClock ? mSmallClockPadding : 0;
         mClockView.setLayoutParams(layoutParams);
-
-        layoutParams = (RelativeLayout.LayoutParams) mClockSeparator.getLayoutParams();
-        layoutParams.topMargin = smallClock ? (int) mWidgetPadding : 0;
-        layoutParams.bottomMargin = layoutParams.topMargin;
-        mClockSeparator.setLayoutParams(layoutParams);
     }
 
     /**
-     * Animate clock and its separator when necessary.
+     * Animate clock when necessary.
      */
     @Override
     public void onLayoutChange(View view, int left, int top, int right, int bottom,
@@ -258,25 +248,6 @@
                 mClockView.setStyle(style);
                 mClockView.invalidate();
             }
-        } else if (view == mClockSeparator) {
-            boolean hasSeparator = hasHeader && !mPulsing;
-            float alpha = hasSeparator ? 1 : 0;
-            mClockSeparator.animate().cancel();
-            if (shouldAnimate) {
-                boolean isAwake = mDarkAmount != 0;
-                mClockSeparator.setY(oldTop + heightOffset);
-                mClockSeparator.animate()
-                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                        .setDuration(duration)
-                        .setListener(isAwake ? null : new KeepAwakeAnimationListener(getContext()))
-                        .setStartDelay(delay)
-                        .y(top)
-                        .alpha(alpha)
-                        .start();
-            } else {
-                mClockSeparator.setY(top);
-                mClockSeparator.setAlpha(alpha);
-            }
         }
     }
 
@@ -291,7 +262,8 @@
 
     @Override
     public void onDensityOrFontScaleChanged() {
-        mWidgetPadding = getResources().getDimension(R.dimen.widget_vertical_padding);
+        mSmallClockPadding = getResources()
+                .getDimensionPixelSize(R.dimen.widget_small_clock_padding);
         if (mClockView != null) {
             mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                     getResources().getDimensionPixelSize(R.dimen.widget_big_font_size));
@@ -352,6 +324,7 @@
             }
         }
         mOwnerInfo.setText(info);
+        updateDark();
     }
 
     @Override
@@ -434,7 +407,6 @@
         updateDozeVisibleViews();
         mKeyguardSlice.setDarkAmount(mDarkAmount);
         mClockView.setTextColor(blendedTextColor);
-        mClockSeparator.setBackgroundColor(blendedTextColor);
     }
 
     private void layoutOwnerInfo() {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 45f1686..08691ec 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -54,7 +54,7 @@
             if (mTextView != null && mTextView.isEnabled()) {
                 mTextView.append(Character.forDigit(mDigit, 10));
             }
-            userActivity();;
+            userActivity();
         }
     };
 
@@ -118,7 +118,7 @@
 
         a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
         if (!a.hasValueOrEmpty(android.R.styleable.View_background)) {
-            setBackground(mContext.getDrawable(R.drawable.ripple_drawable));
+            setBackground(mContext.getDrawable(R.drawable.ripple_drawable_pin));
         }
         a.recycle();
         setContentDescription(mDigitText.getText().toString());
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index e58538d..e1b8dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -29,46 +29,69 @@
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.startPendingIntentDismissingKeyguard(intent);
     }
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.startActivity(intent, dismissShade);
     }
 
     @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.startActivity(intent, onlyProvisioned, dismissShade);
     }
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.startActivity(intent, dismissShade, callback);
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(Intent intent, int delay) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.postStartActivityDismissingKeyguard(intent, delay);
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(PendingIntent intent) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.postStartActivityDismissingKeyguard(intent);
     }
 
     @Override
     public void postQSRunnableDismissingKeyguard(Runnable runnable) {
-        if (mActualStarter == null) return;
+        if (mActualStarter == null) {
+            return;
+        }
         mActualStarter.postQSRunnableDismissingKeyguard(runnable);
     }
 
+    @Override
+    public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancel,
+            boolean afterKeyguardGone) {
+        if (mActualStarter == null) {
+            return;
+        }
+        mActualStarter.dismissKeyguardThenExecute(action, cancel, afterKeyguardGone);
+    }
+
     public void setActivityStarterImpl(ActivityStarter starter) {
         mActualStarter = starter;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 2c821b2..494880e 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -48,16 +48,22 @@
 import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -343,6 +349,14 @@
 
         mProviders.put(LockscreenGestureLogger.class, () -> new LockscreenGestureLogger());
 
+        mProviders.put(KeyguardEnvironment.class, () -> new KeyguardEnvironmentImpl());
+        mProviders.put(ShadeController.class, () ->
+                SysUiServiceProvider.getComponent(mContext, StatusBar.class));
+        mProviders.put(NotificationRemoteInputManager.Callback.class,
+                () -> new StatusBarRemoteInputCallback(mContext));
+
+        mProviders.put(InitController.class, InitController::new);
+
         // Put all dependencies above here so the factory can override them if it wants.
         SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
 
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 198a4e6..b1463a3 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -143,9 +143,6 @@
                 mSeparatedView.setBackground(mSeparatedViewBackground);
                 updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
                 mOldHeight = mList.getMeasuredHeight();
-                mList.addOnLayoutChangeListener(
-                        (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
-                                updatePosition());
                 updateRotation();
             } else {
                 return;
@@ -155,6 +152,8 @@
         if (newHeight != mOldHeight) {
             animateChild(mOldHeight, newHeight);
         }
+
+        post(() -> updatePaddingAndGravityIfTooTall());
         post(() -> updatePosition());
     }
 
@@ -241,7 +240,7 @@
         separatedViewLayoutParams.gravity = rotateGravityRight(separatedViewLayoutParams.gravity);
         mSeparatedView.setLayoutParams(separatedViewLayoutParams);
 
-        setGravity(p.gravity);
+        setGravity(rotateGravityRight(getGravity()));
     }
 
     private void swapDimens(View v) {
@@ -299,7 +298,7 @@
         separatedViewLayoutParams.gravity = rotateGravityLeft(separatedViewLayoutParams.gravity);
         mSeparatedView.setLayoutParams(separatedViewLayoutParams);
 
-        setGravity(p.gravity);
+        setGravity(rotateGravityLeft(getGravity()));
     }
 
     private int rotateGravityLeft(int gravity) {
@@ -447,6 +446,46 @@
         mAnimator.start();
     }
 
+    // If current power menu height larger then screen height, remove padding to break power menu
+    // alignment and set menu center vertical within the screen.
+    private void updatePaddingAndGravityIfTooTall() {
+        int defaultTopPadding;
+        int viewsTotalHeight;
+        int separatedViewTopMargin;
+        int screenHeight;
+        int totalHeight;
+        int targetGravity;
+        MarginLayoutParams params = (MarginLayoutParams) mSeparatedView.getLayoutParams();
+        switch (RotationUtils.getRotation(getContext())) {
+            case RotationUtils.ROTATION_LANDSCAPE:
+                defaultTopPadding = getPaddingLeft();
+                viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
+                separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
+                screenHeight = getMeasuredWidth();
+                targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.TOP;
+                break;
+            case RotationUtils.ROTATION_SEASCAPE:
+                defaultTopPadding = getPaddingRight();
+                viewsTotalHeight = mList.getMeasuredWidth() + mSeparatedView.getMeasuredWidth();
+                separatedViewTopMargin = mHasSeparatedButton ? params.leftMargin : 0;
+                screenHeight = getMeasuredWidth();
+                targetGravity = Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM;
+                break;
+            default: // Portrait
+                defaultTopPadding = getPaddingTop();
+                viewsTotalHeight = mList.getMeasuredHeight() + mSeparatedView.getMeasuredHeight();
+                separatedViewTopMargin = mHasSeparatedButton ? params.topMargin : 0;
+                screenHeight = getMeasuredHeight();
+                targetGravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
+                break;
+        }
+        totalHeight = defaultTopPadding + viewsTotalHeight + separatedViewTopMargin;
+        if (totalHeight >= screenHeight) {
+            setPadding(0, 0, 0, 0);
+            setGravity(targetGravity);
+        }
+    }
+
     @Override
     public ViewOutlineProvider getOutlineProvider() {
         return super.getOutlineProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 77f4bf5..d8eb965 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -30,7 +30,7 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
-import android.view.DisplayListCanvas;
+import android.graphics.RecordingCanvas;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.WindowManager;
@@ -381,7 +381,7 @@
                     try {
                         Bitmap wallpaper = mWallpaperManager.getBitmap(true /* hardware */);
                         if (wallpaper != null
-                                && wallpaper.getByteCount() > DisplayListCanvas.MAX_BITMAP_SIZE) {
+                                && wallpaper.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
                             throw new RuntimeException("Wallpaper is too large to draw!");
                         }
                         return wallpaper;
diff --git a/packages/SystemUI/src/com/android/systemui/InitController.java b/packages/SystemUI/src/com/android/systemui/InitController.java
new file mode 100644
index 0000000..52ba66a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/InitController.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import java.util.ArrayList;
+
+/**
+ * Created by {@link Dependency} on SystemUI startup. Add tasks which need to be executed only
+ * after all other dependencies have been created.
+ */
+public class InitController {
+
+    private final ArrayList<Runnable> mTasks = new ArrayList<>();
+
+    /**
+     * Add a task to be executed after {@link Dependency#start()}
+     * @param runnable the task to be executed
+     */
+    public void addPostInitTask(Runnable runnable) {
+        mTasks.add(runnable);
+    }
+
+    /**
+     * Run post-init tasks and remove them from the tasks list
+     */
+    public void executePostInitTasks() {
+        while (!mTasks.isEmpty()) {
+            mTasks.remove(0).run();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index d351c4f3..1bf8750 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -259,6 +259,7 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             mHandler.removeCallbacks(mDeferredConnectionCallback);
+            mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
             mConnectionBackoffAttempts = 0;
             mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
             // Listen for launcher's death
@@ -269,7 +270,6 @@
             }
             try {
                 mOverviewProxy.onBind(mSysUiProxy);
-                mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
             } catch (RemoteException e) {
                 mCurrentBoundedUserId = -1;
                 Log.e(TAG_OPS, "Failed to call onBind()", e);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 3007b6e..c844496 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -207,11 +207,11 @@
 
         mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         mOverlay.setAlpha(0);
-        mOverlay.setAllowForceDark(false);
+        mOverlay.setForceDarkAllowed(false);
 
         mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
         mBottomOverlay.setAlpha(0);
-        mBottomOverlay.setAllowForceDark(false);
+        mBottomOverlay.setForceDarkAllowed(false);
 
         updateViews();
 
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 0215fda..3fe9944 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -87,6 +87,8 @@
     private Runnable mWatchLongPress;
     private final long mLongPressTimeout;
 
+    protected boolean mSwipingInProgress;
+
     final private int[] mTmpPos = new int[2];
     private final int mFalsingThreshold;
     private boolean mTouchAboveFalsingThreshold;
@@ -127,6 +129,10 @@
         mDisableHwLayers = disableHwLayers;
     }
 
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
     private float getPos(MotionEvent ev) {
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
@@ -318,6 +324,7 @@
                     if (Math.abs(delta) > mPagingTouchSlop
                             && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
                         if (mCallback.canChildBeDragged(mCurrView)) {
+                            mSwipingInProgress = true;
                             mCallback.onBeginDrag(mCurrView);
                             mDragging = true;
                             mInitialTouchPos = getPos(ev);
@@ -437,6 +444,7 @@
                     wasRemoved = row.isRemoved();
                 }
                 if (!mCancelled || wasRemoved) {
+                    mSwipingInProgress = false;
                     mCallback.onChildDismissed(animView);
                 }
                 if (endAction != null) {
@@ -626,6 +634,7 @@
                                 !swipedFastEnough() /* useAccelerateInterpolator */);
                     } else {
                         // snappity
+                        mSwipingInProgress = false;
                         mCallback.onDragCancelled(mCurrView);
                         snapChild(mCurrView, 0 /* leftTarget */, velocity);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 78053b2..92aa652 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -194,6 +194,7 @@
                 mServices[i].onBootCompleted();
             }
         }
+        Dependency.get(InitController.class).executePostInitTasks();
         log.traceEnd();
         Dependency.get(PluginManager.class).addPluginListener(
                 new PluginListener<OverlayPlugin>() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 258b6f6..c4bf27b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -32,6 +32,7 @@
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.AmbientPulseManager;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -133,7 +134,7 @@
             Context context) {
         providers.put(StatusBarStateController.class, StatusBarStateController::new);
         providers.put(NotificationLockscreenUserManager.class,
-                () -> new NotificationLockscreenUserManager(context));
+                () -> new NotificationLockscreenUserManagerImpl(context));
         providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
         providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
         providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 38a90cf..53cdee5 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -202,7 +202,8 @@
 
         // Close Recent Apps if needed
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).animateCollapsePanels(
-                CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL);
+                CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL | CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                false /* force */);
 
         boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 5739c99..96af08b 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -44,7 +44,7 @@
         // Because space is usually constrained in the auto use-case, there should not be a
         // pinned notification when the shade has been expanded. Ensure this by not pinning any
         // notification if the shade is already opened.
-        if (!mPresenter.isPresenterFullyCollapsed()) {
+        if (!getPresenter().isPresenterFullyCollapsed()) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java
new file mode 100644
index 0000000..f34d6b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/car/CarNotificationMediaManager.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.car;
+
+import android.content.Context;
+
+import com.android.systemui.statusbar.NotificationMediaManager;
+
+public class CarNotificationMediaManager extends NotificationMediaManager {
+    public CarNotificationMediaManager(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+        // Do nothing, we don't want to display media art in the lock screen for a car.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index a015a18..e4b2e07 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -22,6 +22,7 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.SystemUIFactory;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.car.CarFacetButtonController;
 import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
@@ -46,5 +47,7 @@
                 () -> new CarNotificationEntryManager(context));
         providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
         providers.put(HvacController.class, () -> new HvacController(context));
+        providers.put(NotificationMediaManager.class,
+                () -> new CarNotificationMediaManager(context));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index bb05980..1e61a77 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -47,6 +47,12 @@
 
     void onIgnoreTouchWhilePulsing(boolean ignore);
 
+    /**
+     * If the device was waken up by a passive interrupt that will show the lock screen without
+     * expanding the notification panel/shade.
+     */
+    void setPassiveInterrupt(boolean lightInterrupt);
+
     interface Callback {
         /**
          * Called when a high priority notification is added.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index cb91d78..d69b1bf 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -142,6 +142,7 @@
                     mDozeHost.onDoubleTap(screenX, screenY);
                     mMachine.wakeUp();
                 } else if (isPickup || isWakeLockScreen) {
+                    mDozeHost.setPassiveInterrupt(true);
                     mMachine.wakeUp();
                 } else {
                     mDozeHost.extendPulse();
@@ -210,6 +211,7 @@
             case INITIALIZED:
                 mBroadcastReceiver.register(mContext);
                 mDozeHost.addCallback(mHostCallback);
+                mDozeHost.setPassiveInterrupt(false);
                 checkTriggersAtInit();
                 break;
             case DOZE:
@@ -219,6 +221,7 @@
                     mDozeSensors.reregisterAllSensors();
                 }
                 mDozeSensors.setListening(true);
+                mDozeHost.setPassiveInterrupt(false);
                 break;
             case DOZE_AOD_PAUSED:
             case DOZE_AOD_PAUSING:
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
index 774567e..95029c0 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -26,6 +26,10 @@
 
 public class PluginInitializerImpl implements PluginInitializer {
 
+    /**
+     * True if WTFs should lead to crashes
+     */
+    private static final boolean WTFS_SHOULD_CRASH = false;
     private boolean mWtfsSet;
 
     @Override
@@ -52,7 +56,7 @@
 
     @Override
     public void handleWtfs() {
-        if (!mWtfsSet) {
+        if (WTFS_SHOULD_CRASH && !mWtfsSet) {
             mWtfsSet = true;
             Log.setWtfHandler(new Log.TerribleFailureHandler() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 92f5cae..15d2e66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -308,6 +308,7 @@
                     @Override
                     public void onClick(View v) {
                         int position = holder.getAdapterPosition();
+                        if (position == RecyclerView.NO_POSITION) return;
                         if (mAccessibilityAction != ACTION_NONE) {
                             selectPosition(position, v);
                         } else {
@@ -561,6 +562,7 @@
             if (viewHolder == mCurrentDrag) return;
             if (mCurrentDrag != null) {
                 int position = mCurrentDrag.getAdapterPosition();
+                if (position == RecyclerView.NO_POSITION) return;
                 TileInfo info = mTiles.get(position);
                 mCurrentDrag.mTileView.setShowAppLabel(
                         position > mEditIndex && !info.isSystem);
@@ -582,13 +584,14 @@
         @Override
         public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
                 ViewHolder target) {
-            if (target.getAdapterPosition() == 0){
+            final int position = target.getAdapterPosition();
+            if (position == 0 || position == RecyclerView.NO_POSITION){
                 return false;
             }
             if (!canRemoveTiles() && current.getAdapterPosition() < mEditIndex) {
-                return target.getAdapterPosition() < mEditIndex;
+                return position < mEditIndex;
             }
-            return target.getAdapterPosition() <= mEditIndex + 1;
+            return position <= mEditIndex + 1;
         }
 
         @Override
@@ -610,6 +613,10 @@
         public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
             int from = viewHolder.getAdapterPosition();
             int to = target.getAdapterPosition();
+            if (from == 0 || from == RecyclerView.NO_POSITION ||
+                    to == 0 || to == RecyclerView.NO_POSITION) {
+                return false;
+            }
             return move(from, to, target.itemView);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
index f217596..5bb5b2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewAccessibilityDelegate.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.recents.views;
 
-import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
@@ -25,14 +24,11 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
 import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
-import com.android.systemui.shared.recents.utilities.Utilities;
-import com.android.systemui.shared.recents.model.TaskStack;
 
 public class TaskViewAccessibilityDelegate extends View.AccessibilityDelegate {
     private static final String TAG = "TaskViewAccessibilityDelegate";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 2c384d0..21a33b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
@@ -85,6 +87,7 @@
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
         }
+        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 5c0b328..daaefb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
+
+import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.hardware.biometrics.IBiometricPromptReceiver;
@@ -24,7 +27,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
+
 import androidx.annotation.VisibleForTesting;
 import android.util.Pair;
 
@@ -117,7 +120,7 @@
         default void removeIcon(String slot) { }
         default void disable(int state1, int state2, boolean animate) { }
         default void animateExpandNotificationsPanel() { }
-        default void animateCollapsePanels(int flags) { }
+        default void animateCollapsePanels(int flags, boolean force) { }
         default void togglePanel() { }
         default void animateExpandSettingsPanel(String obj) { }
         default void setSystemUiVisibility(int vis, int fullscreenStackVis,
@@ -169,7 +172,13 @@
     }
 
     @VisibleForTesting
-    protected CommandQueue() {
+    public CommandQueue() {
+    }
+
+    public boolean panelsEnabled() {
+        return (mDisable1 & StatusBarManager.DISABLE_EXPAND) == 0
+                && (mDisable2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
+                && !ONLY_CORE_APPS;
     }
 
     public void addCallbacks(Callbacks callbacks) {
@@ -234,10 +243,10 @@
         }
     }
 
-    public void animateCollapsePanels(int flags) {
+    public void animateCollapsePanels(int flags, boolean force) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_COLLAPSE_PANELS);
-            mHandler.obtainMessage(MSG_COLLAPSE_PANELS, flags, 0).sendToTarget();
+            mHandler.obtainMessage(MSG_COLLAPSE_PANELS, flags, force ? 1 : 0).sendToTarget();
         }
     }
 
@@ -592,7 +601,7 @@
                     break;
                 case MSG_COLLAPSE_PANELS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).animateCollapsePanels(msg.arg1);
+                        mCallbacks.get(i).animateCollapsePanels(msg.arg1, msg.arg2 != 0);
                     }
                     break;
                 case MSG_TOGGLE_PANEL:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 30d9ef7..8526afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -131,7 +131,7 @@
                 if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,
                         (int) (y - mInitialTouchY))) {
                     if (mStartingChild == null) {
-                        mDragDownCallback.setEmptyDragAmount(0f);
+                        cancelExpansion();
                     } else {
                         mCallback.setUserLockedChild(mStartingChild, false);
                         mStartingChild = null;
@@ -206,11 +206,8 @@
         ValueAnimator anim = ValueAnimator.ofFloat(mLastHeight, 0);
         anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
-        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
-            }
+        anim.addUpdateListener(animation -> {
+            mDragDownCallback.setEmptyDragAmount((Float) animation.getAnimatedValue());
         });
         anim.start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 18151d0..1f57634 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -29,9 +29,9 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
+import android.graphics.RecordingCanvas;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
-import android.view.DisplayListCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.view.ViewAnimationUtils;
@@ -192,8 +192,8 @@
                 // Our hardware drawing proparties can be null if the finishing started but we have
                 // never drawn before. In that case we are not doing a render thread animation
                 // anyway, so we need to use the normal drawing.
-                DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
-                displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
+                RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+                recordingCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
                         mHwCirclePaint);
             } else {
                 updateCircleColor();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 0c5f391..a00eac4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,14 +19,14 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.admin.DevicePolicyManager;
-import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.BatteryManager;
@@ -106,6 +106,7 @@
 
     private final DevicePolicyManager mDevicePolicyManager;
     private boolean mDozing;
+    private float mDarkAmount;
 
     /**
      * Creates a new KeyguardIndicationController and registers callbacks.
@@ -298,6 +299,15 @@
         if (mVisible) {
             // Walk down a precedence-ordered list of what indication
             // should be shown based on user or device state
+            if (mDozing) {
+                if (!TextUtils.isEmpty(mTransientIndication)) {
+                    mTextView.setTextColor(Color.WHITE);
+                    mTextView.switchIndication(mTransientIndication);
+                }
+                updateAlphas();
+                return;
+            }
+
             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
             int userId = KeyguardUpdateMonitor.getCurrentUser();
             String trustGrantedIndication = getTrustGrantedIndication();
@@ -335,6 +345,14 @@
         }
     }
 
+    private void updateAlphas() {
+        if (!TextUtils.isEmpty(mTransientIndication)) {
+            mTextView.setAlpha(1f);
+        } else {
+            mTextView.setAlpha(1f - mDarkAmount);
+        }
+    }
+
     // animates textView - textView moves up and bounces down
     private void animateText(KeyguardIndicationTextView textView, String indication) {
         int yTranslation = mContext.getResources().getInteger(
@@ -492,6 +510,14 @@
         pw.println("  computePowerIndication(): " + computePowerIndication());
     }
 
+    public void setDarkAmount(float darkAmount) {
+        if (mDarkAmount == darkAmount) {
+            return;
+        }
+        mDarkAmount = darkAmount;
+        updateAlphas();
+    }
+
     protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
         public static final int HIDE_DELAY_MS = 5000;
         private int mLastSuccessiveErrorMessage = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index cfa09bc..f3a46ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -29,6 +29,7 @@
 
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
 
 /**
@@ -41,11 +42,14 @@
     // Dependencies:
     private final NotificationRemoteInputManager mRemoteInputManager =
             Dependency.get(NotificationRemoteInputManager.class);
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
 
     private final Context mContext;
 
     protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
 
     public NotificationListener(Context context) {
         mContext = context;
@@ -61,7 +65,7 @@
             return;
         }
         final RankingMap currentRanking = getCurrentRanking();
-        mPresenter.getHandler().post(() -> {
+        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
             for (StatusBarNotification sbn : notifications) {
                 mEntryManager.addNotification(sbn, currentRanking);
             }
@@ -73,7 +77,7 @@
             final RankingMap rankingMap) {
         if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
         if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
-            mPresenter.getHandler().post(() -> {
+            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                 processForRemoteInput(sbn.getNotification(), mContext);
                 String key = sbn.getKey();
                 boolean isUpdate =
@@ -83,7 +87,7 @@
                 // anyway. This is true also when the summary is canceled,
                 // because children are automatically canceled by NoMan in that case.
                 if (!ENABLE_CHILD_NOTIFICATIONS
-                        && mPresenter.getGroupManager().isChildInGroupWithSummary(sbn)) {
+                        && mGroupManager.isChildInGroupWithSummary(sbn)) {
                     if (DEBUG) {
                         Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
                     }
@@ -112,7 +116,7 @@
         if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
         if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
             final String key = sbn.getKey();
-            mPresenter.getHandler().post(() -> {
+            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                 mEntryManager.removeNotification(key, rankingMap);
             });
         }
@@ -123,16 +127,14 @@
         if (DEBUG) Log.d(TAG, "onRankingUpdate");
         if (rankingMap != null) {
             RankingMap r = onPluginRankingUpdate(rankingMap);
-            mPresenter.getHandler().post(() -> {
+            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                 mEntryManager.updateNotificationRanking(r);
             });
         }
     }
 
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager) {
+    public void setUpWithPresenter(NotificationPresenter presenter) {
         mPresenter = presenter;
-        mEntryManager = entryManager;
 
         try {
             registerAsSystemService(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 89a842e..bc662e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -1,516 +1,61 @@
 /*
  * Copyright (C) 2017 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
  */
+
 package com.android.systemui.statusbar;
 
-import android.app.ActivityManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
 import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.util.Log;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
-import com.android.systemui.Dumpable;
-import com.android.systemui.OverviewProxyService;
-import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Handles keeping track of the current user, profiles, and various things related to hiding
- * contents, redacting notifications, and the lockscreen.
- */
-public class NotificationLockscreenUserManager implements Dumpable, StateListener {
-    private static final String TAG = "LockscreenUserManager";
-    private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
-    public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
-    public static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
+public interface NotificationLockscreenUserManager {
+    String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+    String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
 
-    private final DevicePolicyManager mDevicePolicyManager;
-    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
-    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
-    private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
-    private final DeviceProvisionedController mDeviceProvisionedController =
-            Dependency.get(DeviceProvisionedController.class);
-    private final UserManager mUserManager;
-    private final IStatusBarService mBarService;
-    private final LockPatternUtils mLockPatternUtils;
-    private final KeyguardManager mKeyguardManager;
-    private StatusBarKeyguardViewManager mKeyguardViewManager;
-
-    private boolean mShowLockscreenNotifications;
-    private boolean mAllowLockscreenRemoteInput;
-    private int mState = StatusBarState.SHADE;
-
-    protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-
-            if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
-                    isCurrentProfile(getSendingUserId())) {
-                mUsersAllowingPrivateNotifications.clear();
-                updateLockscreenNotificationSetting();
-                mEntryManager.updateNotifications();
-            } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
-                if (userId != mCurrentUserId && isCurrentProfile(userId)) {
-                    updatePublicMode();
-                    mPresenter.onWorkChallengeChanged();
-                    mEntryManager.updateNotifications();
-                }
-            }
-        }
-    };
-
-    protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                updateCurrentProfilesCache();
-                Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
-
-                updateLockscreenNotificationSetting();
-                updatePublicMode();
-                mPresenter.onUserSwitched(mCurrentUserId);
-                mEntryManager.getNotificationData().filterAndSort();
-            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
-                updateCurrentProfilesCache();
-            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                // Start the overview connection to the launcher service
-                Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
-            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
-                final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
-                final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
-                if (intentSender != null) {
-                    try {
-                        mContext.startIntentSender(intentSender, null, 0, 0, 0);
-                    } catch (IntentSender.SendIntentException e) {
-                        /* ignore */
-                    }
-                }
-                if (notificationKey != null) {
-                    final int count =
-                            mEntryManager.getNotificationData().getActiveNotifications().size();
-                    final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
-                    final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
-                            rank, count, true);
-                    try {
-                        mBarService.onNotificationClick(notificationKey, nv);
-                    } catch (RemoteException e) {
-                        /* ignore */
-                    }
-                }
-            }
-        }
-    };
-
-    protected final Context mContext;
-    protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
-
-    protected int mCurrentUserId = 0;
-    protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
-    protected ContentObserver mLockscreenSettingsObserver;
-    protected ContentObserver mSettingsObserver;
-
-    public NotificationLockscreenUserManager(Context context) {
-        mContext = context;
-        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
-                Context.DEVICE_POLICY_SERVICE);
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mCurrentUserId = ActivityManager.getCurrentUser();
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mLockPatternUtils = new LockPatternUtils(mContext);
-        mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        Dependency.get(StatusBarStateController.class).addListener(this);
-    }
-
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager) {
-        mPresenter = presenter;
-        mEntryManager = entryManager;
-
-        mLockscreenSettingsObserver = new ContentObserver(mPresenter.getHandler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
-                // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
-                mUsersAllowingPrivateNotifications.clear();
-                mUsersAllowingNotifications.clear();
-                // ... and refresh all the notifications
-                updateLockscreenNotificationSetting();
-                mEntryManager.updateNotifications();
-            }
-        };
-
-        mSettingsObserver = new ContentObserver(mPresenter.getHandler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                updateLockscreenNotificationSetting();
-                if (mDeviceProvisionedController.isDeviceProvisioned()) {
-                    mEntryManager.updateNotifications();
-                }
-            }
-        };
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
-                mLockscreenSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
-                true,
-                mLockscreenSettingsObserver,
-                UserHandle.USER_ALL);
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
-                mSettingsObserver);
-
-        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
-                    false,
-                    mSettingsObserver,
-                    UserHandle.USER_ALL);
-        }
-
-        IntentFilter allUsersFilter = new IntentFilter();
-        allUsersFilter.addAction(
-                DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
-        allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
-        mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
-                null, null);
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_USER_ADDED);
-        filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiver(mBaseBroadcastReceiver, filter);
-
-        IntentFilter internalFilter = new IntentFilter();
-        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
-        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
-
-        updateCurrentProfilesCache();
-
-        mSettingsObserver.onChange(false);  // set up
-    }
-
-    public boolean shouldShowLockscreenNotifications() {
-        return mShowLockscreenNotifications;
-    }
-
-    public boolean shouldAllowLockscreenRemoteInput() {
-        return mAllowLockscreenRemoteInput;
-    }
-
-    public boolean isCurrentProfile(int userId) {
-        synchronized (mCurrentProfiles) {
-            return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
-        }
-    }
-
-    public void setKeyguardViewManager(StatusBarKeyguardViewManager sbkvm) {
-        mKeyguardViewManager = sbkvm;
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        mState = newState;
-        updatePublicMode();
-    }
-
-    public void updatePublicMode() {
-        //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
-        // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
-        // asking if the keyguard is showing. We still need to check it though because showing the
-        // camera on the keyguard has a state of SHADE but the keyguard is still showing.
-        boolean showingKeyguard = mState != StatusBarState.SHADE
-                || mKeyguardViewManager.isShowing();
-        boolean devicePublic = showingKeyguard && mKeyguardViewManager.isSecure(getCurrentUserId());
-
-        SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
-        for (int i = currentProfiles.size() - 1; i >= 0; i--) {
-            final int userId = currentProfiles.valueAt(i).id;
-            boolean isProfilePublic = devicePublic;
-            if (!devicePublic && userId != mCurrentUserId) {
-                // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
-                // due to a race condition where this code could be called before
-                // TrustManagerService updates its internal records, resulting in an incorrect
-                // state being cached in mLockscreenPublicMode. (b/35951989)
-                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
-                        && mKeyguardViewManager.isSecure(userId)) {
-                    isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
-                }
-            }
-            setLockscreenPublicMode(isProfilePublic, userId);
-        }
-    }
+    boolean shouldAllowLockscreenRemoteInput();
 
     /**
-     * Returns true if notifications are temporarily disabled for this user for security reasons,
-     * regardless of the normal settings for that user.
+     * @param userId user Id
+     * @return true if we re on a secure lock screen
      */
-    private boolean shouldTemporarilyHideNotifications(int userId) {
-        if (userId == UserHandle.USER_ALL) {
-            userId = mCurrentUserId;
-        }
-        return KeyguardUpdateMonitor.getInstance(mContext).isUserInLockdown(userId);
-    }
+    boolean isLockscreenPublicMode(int userId);
 
-    /**
-     * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
-     * If so, notifications should be hidden.
-     */
-    public boolean shouldHideNotifications(int userId) {
-        return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
-                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId))
-                || shouldTemporarilyHideNotifications(userId);
-    }
+    void setUpWithPresenter(NotificationPresenter presenter);
 
-    /**
-     * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
-     * package-specific override.
-     */
-    public boolean shouldHideNotifications(String key) {
-        if (mEntryManager == null) {
-            Log.wtf(TAG, "mEntryManager was null!", new Throwable());
-            return true;
-        }
-        return isLockscreenPublicMode(mCurrentUserId)
-                && mEntryManager.getNotificationData().getVisibilityOverride(key) ==
-                        Notification.VISIBILITY_SECRET;
-    }
+    int getCurrentUserId();
 
-    public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
-        if (mEntryManager == null) {
-            Log.wtf(TAG, "mEntryManager was null!", new Throwable());
-            return false;
-        }
-        return mShowLockscreenNotifications
-                && !mEntryManager.getNotificationData().isAmbient(sbn.getKey());
-    }
+    boolean isCurrentProfile(int userId);
 
-    private void setShowLockscreenNotifications(boolean show) {
-        mShowLockscreenNotifications = show;
-    }
+    void destroy();
 
-    private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
-        mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
-    }
+    SparseArray<UserInfo> getCurrentProfiles();
 
-    protected void updateLockscreenNotificationSetting() {
-        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                1,
-                mCurrentUserId) != 0;
-        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
-                null /* admin */, mCurrentUserId);
-        final boolean allowedByDpm = (dpmFlags
-                & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+    void setLockscreenPublicMode(boolean isProfilePublic, int userId);
 
-        setShowLockscreenNotifications(show && allowedByDpm);
+    boolean shouldShowLockscreenNotifications();
 
-        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
-            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
-                    0,
-                    mCurrentUserId) != 0;
-            final boolean remoteInputDpm =
-                    (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
+    boolean shouldHideNotifications(int userId);
+    boolean shouldHideNotifications(String key);
+    boolean shouldShowOnKeyguard(StatusBarNotification sbn);
 
-            setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm);
-        } else {
-            setLockscreenAllowRemoteInput(false);
-        }
-    }
+    boolean isAnyProfilePublicMode();
 
-    /**
-     * Has the given user chosen to allow their private (full) notifications to be shown even
-     * when the lockscreen is in "public" (secure & locked) mode?
-     */
-    public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
-        if (userHandle == UserHandle.USER_ALL) {
-            return true;
-        }
+    void updatePublicMode();
 
-        if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
-            final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
-                    DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-            final boolean allowed = allowedByUser && allowedByDpm;
-            mUsersAllowingPrivateNotifications.append(userHandle, allowed);
-            return allowed;
-        }
+    boolean needsRedaction(Entry entry);
 
-        return mUsersAllowingPrivateNotifications.get(userHandle);
-    }
-
-    private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
-        if (userHandle == UserHandle.USER_ALL) {
-            return true;
-        }
-        final int dpmFlags =
-                mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle);
-        return (dpmFlags & feature) == 0;
-    }
-
-    /**
-     * Save the current "public" (locked and secure) state of the lockscreen.
-     */
-    public void setLockscreenPublicMode(boolean publicMode, int userId) {
-        mLockscreenPublicMode.put(userId, publicMode);
-    }
-
-    public boolean isLockscreenPublicMode(int userId) {
-        if (userId == UserHandle.USER_ALL) {
-            return mLockscreenPublicMode.get(mCurrentUserId, false);
-        }
-        return mLockscreenPublicMode.get(userId, false);
-    }
-
-    /**
-     * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
-     * "public" (secure & locked) mode?
-     */
-    private boolean userAllowsNotificationsInPublic(int userHandle) {
-        if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
-            return true;
-        }
-
-        if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
-            final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
-                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-            final boolean allowed = allowedByUser && allowedByDpm;
-            mUsersAllowingNotifications.append(userHandle, allowed);
-            return allowed;
-        }
-
-        return mUsersAllowingNotifications.get(userHandle);
-    }
-
-    /** @return true if the entry needs redaction when on the lockscreen. */
-    public boolean needsRedaction(NotificationData.Entry ent) {
-        int userId = ent.notification.getUserId();
-
-        boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
-        boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
-        boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
-
-        boolean notificationRequestsRedaction =
-                ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
-        boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
-
-        return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
-    }
-
-    private boolean packageHasVisibilityOverride(String key) {
-        if (mEntryManager == null) {
-            Log.wtf(TAG, "mEntryManager was null!", new Throwable());
-            return true;
-        }
-        return mEntryManager.getNotificationData().getVisibilityOverride(key) ==
-                Notification.VISIBILITY_PRIVATE;
-    }
-
-    private void updateCurrentProfilesCache() {
-        synchronized (mCurrentProfiles) {
-            mCurrentProfiles.clear();
-            if (mUserManager != null) {
-                for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
-                    mCurrentProfiles.put(user.id, user);
-                }
-            }
-        }
-    }
-
-    public boolean isAnyProfilePublicMode() {
-        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns the current user id. This can change if the user is switched.
-     */
-    public int getCurrentUserId() {
-        return mCurrentUserId;
-    }
-
-    public SparseArray<UserInfo> getCurrentProfiles() {
-        return mCurrentProfiles;
-    }
-
-    public void destroy() {
-        mContext.unregisterReceiver(mBaseBroadcastReceiver);
-        mContext.unregisterReceiver(mAllUsersReceiver);
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("NotificationLockscreenUserManager state:");
-        pw.print("  mCurrentUserId=");
-        pw.println(mCurrentUserId);
-        pw.print("  mShowLockscreenNotifications=");
-        pw.println(mShowLockscreenNotifications);
-        pw.print("  mAllowLockscreenRemoteInput=");
-        pw.println(mAllowLockscreenRemoteInput);
-        pw.print("  mCurrentProfiles=");
-        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-            final int userId = mCurrentProfiles.valueAt(i).id;
-            pw.print("" + userId + " ");
-        }
-        pw.println();
-    }
+    boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
new file mode 100644
index 0000000..178c5c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar;
+
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.OverviewProxyService;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Handles keeping track of the current user, profiles, and various things related to hiding
+ * contents, redacting notifications, and the lockscreen.
+ */
+public class NotificationLockscreenUserManagerImpl implements
+        Dumpable, NotificationLockscreenUserManager, StateListener {
+    private static final String TAG = "LockscreenUserManager";
+    private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
+
+    private final DeviceProvisionedController mDeviceProvisionedController =
+            Dependency.get(DeviceProvisionedController.class);
+    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+
+    // Lazy
+    private NotificationEntryManager mEntryManager;
+
+    private final DevicePolicyManager mDevicePolicyManager;
+    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
+    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
+    private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
+    private final UserManager mUserManager;
+    private final IStatusBarService mBarService;
+
+    private boolean mShowLockscreenNotifications;
+    private boolean mAllowLockscreenRemoteInput;
+    private LockPatternUtils mLockPatternUtils;
+    protected KeyguardManager mKeyguardManager;
+    private int mState = StatusBarState.SHADE;
+
+    protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+
+            if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
+                    isCurrentProfile(getSendingUserId())) {
+                mUsersAllowingPrivateNotifications.clear();
+                updateLockscreenNotificationSetting();
+                getEntryManager().updateNotifications();
+            }
+        }
+    };
+
+    protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                updateCurrentProfilesCache();
+                Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+
+                updateLockscreenNotificationSetting();
+                updatePublicMode();
+                mPresenter.onUserSwitched(mCurrentUserId);
+                getEntryManager().getNotificationData().filterAndSort();
+            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+                updateCurrentProfilesCache();
+            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                // Start the overview connection to the launcher service
+                Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
+            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
+                final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+                final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
+                if (intentSender != null) {
+                    try {
+                        mContext.startIntentSender(intentSender, null, 0, 0, 0);
+                    } catch (IntentSender.SendIntentException e) {
+                        /* ignore */
+                    }
+                }
+                if (notificationKey != null) {
+                    final int count =
+                            getEntryManager().getNotificationData().getActiveNotifications().size();
+                    final int rank = getEntryManager().getNotificationData().getRank(notificationKey);
+                    final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
+                            rank, count, true);
+                    try {
+                        mBarService.onNotificationClick(notificationKey, nv);
+                    } catch (RemoteException e) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    };
+
+    protected final Context mContext;
+    protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
+
+    protected int mCurrentUserId = 0;
+    protected NotificationPresenter mPresenter;
+    protected ContentObserver mLockscreenSettingsObserver;
+    protected ContentObserver mSettingsObserver;
+
+    private NotificationEntryManager getEntryManager() {
+        if (mEntryManager == null) {
+            mEntryManager = Dependency.get(NotificationEntryManager.class);
+        }
+        return mEntryManager;
+    }
+
+    public NotificationLockscreenUserManagerImpl(Context context) {
+        mContext = context;
+        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mCurrentUserId = ActivityManager.getCurrentUser();
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        Dependency.get(StatusBarStateController.class).addListener(this);
+        mLockPatternUtils = new LockPatternUtils(context);
+        mKeyguardManager = context.getSystemService(KeyguardManager.class);
+    }
+
+    public void setUpWithPresenter(NotificationPresenter presenter) {
+        mPresenter = presenter;
+
+        mLockscreenSettingsObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+            @Override
+            public void onChange(boolean selfChange) {
+                // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
+                // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
+                mUsersAllowingPrivateNotifications.clear();
+                mUsersAllowingNotifications.clear();
+                // ... and refresh all the notifications
+                updateLockscreenNotificationSetting();
+                getEntryManager().updateNotifications();
+            }
+        };
+
+        mSettingsObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateLockscreenNotificationSetting();
+                if (mDeviceProvisionedController.isDeviceProvisioned()) {
+                    getEntryManager().updateNotifications();
+                }
+            }
+        };
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+                mLockscreenSettingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                true,
+                mLockscreenSettingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
+                mSettingsObserver);
+
+        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+                    false,
+                    mSettingsObserver,
+                    UserHandle.USER_ALL);
+        }
+
+        mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL,
+                new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+                null, null);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        filter.addAction(Intent.ACTION_USER_ADDED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiver(mBaseBroadcastReceiver, filter);
+
+        IntentFilter internalFilter = new IntentFilter();
+        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
+        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+
+        updateCurrentProfilesCache();
+
+        mSettingsObserver.onChange(false);  // set up
+    }
+
+    public boolean shouldShowLockscreenNotifications() {
+        return mShowLockscreenNotifications;
+    }
+
+    public boolean shouldAllowLockscreenRemoteInput() {
+        return mAllowLockscreenRemoteInput;
+    }
+
+    public boolean isCurrentProfile(int userId) {
+        synchronized (mCurrentProfiles) {
+            return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
+        }
+    }
+
+    /**
+     * Returns true if notifications are temporarily disabled for this user for security reasons,
+     * regardless of the normal settings for that user.
+     */
+    private boolean shouldTemporarilyHideNotifications(int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            userId = mCurrentUserId;
+        }
+        return KeyguardUpdateMonitor.getInstance(mContext).isUserInLockdown(userId);
+    }
+
+    /**
+     * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
+     * If so, notifications should be hidden.
+     */
+    public boolean shouldHideNotifications(int userId) {
+        return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
+                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId))
+                || shouldTemporarilyHideNotifications(userId);
+    }
+
+    /**
+     * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
+     * package-specific override.
+     */
+    public boolean shouldHideNotifications(String key) {
+        if (getEntryManager() == null) {
+            Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+            return true;
+        }
+        return isLockscreenPublicMode(mCurrentUserId)
+                && getEntryManager().getNotificationData().getVisibilityOverride(key) ==
+                        Notification.VISIBILITY_SECRET;
+    }
+
+    public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+        if (getEntryManager() == null) {
+            Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+            return false;
+        }
+        return mShowLockscreenNotifications
+                && !getEntryManager().getNotificationData().isAmbient(sbn.getKey());
+    }
+
+    private void setShowLockscreenNotifications(boolean show) {
+        mShowLockscreenNotifications = show;
+    }
+
+    private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
+        mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
+    }
+
+    protected void updateLockscreenNotificationSetting() {
+        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                1,
+                mCurrentUserId) != 0;
+        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
+                null /* admin */, mCurrentUserId);
+        final boolean allowedByDpm = (dpmFlags
+                & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+
+        setShowLockscreenNotifications(show && allowedByDpm);
+
+        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+                    0,
+                    mCurrentUserId) != 0;
+            final boolean remoteInputDpm =
+                    (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
+
+            setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm);
+        } else {
+            setLockscreenAllowRemoteInput(false);
+        }
+    }
+
+    /**
+     * Has the given user chosen to allow their private (full) notifications to be shown even
+     * when the lockscreen is in "public" (secure & locked) mode?
+     */
+    public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
+        if (userHandle == UserHandle.USER_ALL) {
+            return true;
+        }
+
+        if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
+            final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
+                    DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+            final boolean allowed = allowedByUser && allowedByDpm;
+            mUsersAllowingPrivateNotifications.append(userHandle, allowed);
+            return allowed;
+        }
+
+        return mUsersAllowingPrivateNotifications.get(userHandle);
+    }
+
+    private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
+        if (userHandle == UserHandle.USER_ALL) {
+            return true;
+        }
+        final int dpmFlags =
+                mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle);
+        return (dpmFlags & feature) == 0;
+    }
+
+    /**
+     * Save the current "public" (locked and secure) state of the lockscreen.
+     */
+    public void setLockscreenPublicMode(boolean publicMode, int userId) {
+        mLockscreenPublicMode.put(userId, publicMode);
+    }
+
+    public boolean isLockscreenPublicMode(int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            return mLockscreenPublicMode.get(mCurrentUserId, false);
+        }
+        return mLockscreenPublicMode.get(userId, false);
+    }
+
+    /**
+     * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
+     * "public" (secure & locked) mode?
+     */
+    private boolean userAllowsNotificationsInPublic(int userHandle) {
+        if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
+            return true;
+        }
+
+        if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
+            final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
+                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+            final boolean allowed = allowedByUser && allowedByDpm;
+            mUsersAllowingNotifications.append(userHandle, allowed);
+            return allowed;
+        }
+
+        return mUsersAllowingNotifications.get(userHandle);
+    }
+
+    /** @return true if the entry needs redaction when on the lockscreen. */
+    public boolean needsRedaction(NotificationData.Entry ent) {
+        int userId = ent.notification.getUserId();
+
+        boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
+        boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
+        boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
+
+        boolean notificationRequestsRedaction =
+                ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+        boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
+
+        return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
+    }
+
+    private boolean packageHasVisibilityOverride(String key) {
+        if (getEntryManager() == null) {
+            Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+            return true;
+        }
+        return getEntryManager().getNotificationData().getVisibilityOverride(key) ==
+                Notification.VISIBILITY_PRIVATE;
+    }
+
+    private void updateCurrentProfilesCache() {
+        synchronized (mCurrentProfiles) {
+            mCurrentProfiles.clear();
+            if (mUserManager != null) {
+                for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
+                    mCurrentProfiles.put(user.id, user);
+                }
+            }
+        }
+    }
+
+    public boolean isAnyProfilePublicMode() {
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the current user id. This can change if the user is switched.
+     */
+    public int getCurrentUserId() {
+        return mCurrentUserId;
+    }
+
+    public SparseArray<UserInfo> getCurrentProfiles() {
+        return mCurrentProfiles;
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+        mState = newState;
+        updatePublicMode();
+    }
+
+    public void updatePublicMode() {
+        //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
+        // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
+        // asking if the keyguard is showing. We still need to check it though because showing the
+        // camera on the keyguard has a state of SHADE but the keyguard is still showing.
+        final boolean showingKeyguard = mState != StatusBarState.SHADE
+              || mKeyguardMonitor.isShowing();
+        final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
+
+
+        // Look for public mode users. Users are considered public in either case of:
+        //   - device keyguard is shown in secure mode;
+        //   - profile is locked with a work challenge.
+        SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
+        for (int i = currentProfiles.size() - 1; i >= 0; i--) {
+            final int userId = currentProfiles.valueAt(i).id;
+            boolean isProfilePublic = devicePublic;
+            if (!devicePublic && userId != getCurrentUserId()) {
+                // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
+                // due to a race condition where this code could be called before
+                // TrustManagerService updates its internal records, resulting in an incorrect
+                // state being cached in mLockscreenPublicMode. (b/35951989)
+                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+                        && isSecure(userId)) {
+                    isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
+                }
+            }
+            setLockscreenPublicMode(isProfilePublic, userId);
+        }
+    }
+
+
+//    public void updatePublicMode() {
+//        //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
+//        // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
+//        // asking if the keyguard is showing. We still need to check it though because showing the
+//        // camera on the keyguard has a state of SHADE but the keyguard is still showing.
+//        final boolean showingKeyguard = mState != StatusBarState.SHADE
+//              || mKeyguardMonitor.isShowing();
+//        final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
+//
+//
+//        // Look for public mode users. Users are considered public in either case of:
+//        //   - device keyguard is shown in secure mode;
+//        //   - profile is locked with a work challenge.
+//        SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
+//        for (int i = currentProfiles.size() - 1; i >= 0; i--) {
+//            final int userId = currentProfiles.valueAt(i).id;
+//            boolean isProfilePublic = devicePublic;
+//            if (!devicePublic && userId != getCurrentUserId()) {
+//                // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
+//                // due to a race condition where this code could be called before
+//                // TrustManagerService updates its internal records, resulting in an incorrect
+//                // state being cached in mLockscreenPublicMode. (b/35951989)
+//                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+//                        && isSecure(userId)) {
+//                    isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
+//                }
+//            }
+//            setLockscreenPublicMode(isProfilePublic, userId);
+//        }
+//    }
+
+    private boolean isSecure(int userId) {
+        return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId);
+    }
+
+    public void destroy() {
+        mContext.unregisterReceiver(mBaseBroadcastReceiver);
+        mContext.unregisterReceiver(mAllUsersReceiver);
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NotificationLockscreenUserManager state:");
+        pw.print("  mCurrentUserId=");
+        pw.println(mCurrentUserId);
+        pw.print("  mShowLockscreenNotifications=");
+        pw.println(mShowLockscreenNotifications);
+        pw.print("  mAllowLockscreenRemoteInput=");
+        pw.println(mAllowLockscreenRemoteInput);
+        pw.print("  mCurrentProfiles=");
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            final int userId = mCurrentProfiles.valueAt(i).id;
+            pw.print("" + userId + " ");
+        }
+        pw.println();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index e89e6e8..c437b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,19 +15,46 @@
  */
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.Dependency.MAIN_HANDLER;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
+import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
+import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
+
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ScrimState;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -42,15 +69,45 @@
     private static final String TAG = "NotificationMediaManager";
     public static final boolean DEBUG_MEDIA = false;
 
+    private final StatusBarStateController mStatusBarStateController
+            = Dependency.get(StatusBarStateController.class);
+    private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+
+    // Late binding
+    private NotificationEntryManager mEntryManager;
+
+    // Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package
+    @Nullable
+    private ShadeController mShadeController;
+    @Nullable
+    private StatusBarWindowController mStatusBarWindowController;
+
+    @Nullable
+    private BiometricUnlockController mBiometricUnlockController;
+    @Nullable
+    private ScrimController mScrimController;
+    @Nullable
+    private LockscreenWallpaper mLockscreenWallpaper;
+
+    protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+    protected final PorterDuffXfermode mSrcOverXferMode =
+            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
+
+    private final Handler mHandler = Dependency.get(MAIN_HANDLER);
+
     private final Context mContext;
     private final MediaSessionManager mMediaSessionManager;
 
     protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
     private MediaController mMediaController;
     private String mMediaNotificationKey;
     private MediaMetadata mMediaMetadata;
 
+    private BackDropView mBackdrop;
+    private ImageView mBackdropFront;
+    private ImageView mBackdropBack;
+
     private final MediaController.Callback mMediaListener = new MediaController.Callback() {
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
@@ -77,6 +134,29 @@
         }
     };
 
+    @Nullable
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
+    @Nullable
+    private StatusBarWindowController getWindowController() {
+        if (mStatusBarWindowController == null) {
+            mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
+        }
+        return mStatusBarWindowController;
+    }
+
+    private NotificationEntryManager getEntryManager() {
+        if (mEntryManager == null) {
+            mEntryManager = Dependency.get(NotificationEntryManager.class);
+        }
+        return mEntryManager;
+    }
+
     public NotificationMediaManager(Context context) {
         mContext = context;
         mMediaSessionManager
@@ -85,10 +165,8 @@
         // in session state
     }
 
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager) {
+    public void setUpWithPresenter(NotificationPresenter presenter) {
         mPresenter = presenter;
-        mEntryManager = entryManager;
     }
 
     public void onNotificationRemoved(String key) {
@@ -109,8 +187,9 @@
     public void findAndUpdateMediaNotifications() {
         boolean metaDataChanged = false;
 
-        synchronized (mEntryManager.getNotificationData()) {
-            ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
+        NotificationEntryManager manager = getEntryManager();
+        synchronized (manager.getNotificationData()) {
+            ArrayList<NotificationData.Entry> activeNotifications = manager
                     .getNotificationData().getActiveNotifications();
             final int N = activeNotifications.size();
 
@@ -199,7 +278,7 @@
         }
 
         if (metaDataChanged) {
-            mEntryManager.updateNotifications();
+            getEntryManager().updateNotifications();
         }
         mPresenter.updateMediaMetaData(metaDataChanged, true);
     }
@@ -256,9 +335,9 @@
 
     private boolean isMediaNotification(NotificationData.Entry entry) {
         // TODO: confirm that there's a valid media key
-        return entry.getExpandedContentView() != null &&
-                entry.getExpandedContentView()
-                        .findViewById(com.android.internal.R.id.media_actions) != null;
+        return entry.row.getExpandedContentView() != null
+                && entry.row.getExpandedContentView().findViewById(
+                        com.android.internal.R.id.media_actions) != null;
     }
 
     private void clearCurrentMediaNotificationSession() {
@@ -272,4 +351,204 @@
         }
         mMediaController = null;
     }
+
+    /**
+     * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+     */
+    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+        Trace.beginSection("StatusBar#updateMediaMetaData");
+        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
+            Trace.endSection();
+            return;
+        }
+
+        if (mBackdrop == null) {
+            Trace.endSection();
+            return; // called too early
+        }
+
+        boolean wakeAndUnlock = mBiometricUnlockController != null
+            && mBiometricUnlockController.isWakeAndUnlock();
+        if (mKeyguardMonitor.isLaunchTransitionFadingAway() || wakeAndUnlock) {
+            mBackdrop.setVisibility(View.INVISIBLE);
+            Trace.endSection();
+            return;
+        }
+
+        MediaMetadata mediaMetadata = getMediaMetadata();
+
+        if (DEBUG_MEDIA) {
+            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
+                    + getMediaNotificationKey()
+                    + " metadata=" + mediaMetadata
+                    + " metaDataChanged=" + metaDataChanged
+                    + " state=" + mStatusBarStateController.getState());
+        }
+
+        Drawable artworkDrawable = null;
+        if (mediaMetadata != null) {
+            Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+            if (artworkBitmap == null) {
+                artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+                // might still be null
+            }
+            if (artworkBitmap != null) {
+                artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
+            }
+        }
+        boolean allowWhenShade = false;
+        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
+            Bitmap lockWallpaper =
+                    mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
+            if (lockWallpaper != null) {
+                artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
+                        mBackdropBack.getResources(), lockWallpaper);
+                // We're in the SHADE mode on the SIM screen - yet we still need to show
+                // the lockscreen wallpaper in that mode.
+                allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
+            }
+        }
+
+        boolean hideBecauseOccluded = getShadeController() != null
+                && getShadeController().isOccluded();
+
+        final boolean hasArtwork = artworkDrawable != null;
+        mColorExtractor.setHasBackdrop(hasArtwork);
+        if (mScrimController != null) {
+            mScrimController.setHasBackdrop(hasArtwork);
+        }
+
+        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
+                && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
+                &&  mBiometricUnlockController != null && mBiometricUnlockController.getMode()
+                        != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && !hideBecauseOccluded) {
+            // time to show some art!
+            if (mBackdrop.getVisibility() != View.VISIBLE) {
+                mBackdrop.setVisibility(View.VISIBLE);
+                if (allowEnterAnimation) {
+                    mBackdrop.setAlpha(0);
+                    mBackdrop.animate().alpha(1f);
+                } else {
+                    mBackdrop.animate().cancel();
+                    mBackdrop.setAlpha(1f);
+                }
+                if (getWindowController() != null) {
+                    getWindowController().setBackdropShowing(true);
+                }
+                metaDataChanged = true;
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
+                }
+            }
+            if (metaDataChanged) {
+                if (mBackdropBack.getDrawable() != null) {
+                    Drawable drawable =
+                            mBackdropBack.getDrawable().getConstantState()
+                                    .newDrawable(mBackdropFront.getResources()).mutate();
+                    mBackdropFront.setImageDrawable(drawable);
+                    mBackdropFront.setAlpha(1f);
+                    mBackdropFront.setVisibility(View.VISIBLE);
+                } else {
+                    mBackdropFront.setVisibility(View.INVISIBLE);
+                }
+
+                if (DEBUG_MEDIA_FAKE_ARTWORK) {
+                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
+                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
+                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
+                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
+                } else {
+                    mBackdropBack.setImageDrawable(artworkDrawable);
+                }
+
+                if (mBackdropFront.getVisibility() == View.VISIBLE) {
+                    if (DEBUG_MEDIA) {
+                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
+                                + mBackdropFront.getDrawable()
+                                + " to "
+                                + mBackdropBack.getDrawable());
+                    }
+                    mBackdropFront.animate()
+                            .setDuration(250)
+                            .alpha(0f).withEndAction(mHideBackdropFront);
+                }
+            }
+        } else {
+            // need to hide the album art, either because we are unlocked, on AOD
+            // or because the metadata isn't there to support it
+            if (mBackdrop.getVisibility() != View.GONE) {
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
+                }
+                boolean cannotAnimateDoze = getShadeController() != null
+                        && getShadeController().isDozing()
+                        && !ScrimState.AOD.getAnimateChange();
+                if (mBiometricUnlockController != null && mBiometricUnlockController.getMode()
+                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                        || hideBecauseOccluded || cannotAnimateDoze) {
+
+                    // We are unlocking directly - no animation!
+                    mBackdrop.setVisibility(View.GONE);
+                    mBackdropBack.setImageDrawable(null);
+                    if (getWindowController() != null) {
+                        getWindowController().setBackdropShowing(false);
+                    }
+                } else {
+                    if (getWindowController() != null) {
+                        getWindowController().setBackdropShowing(false);
+                    }
+                    mBackdrop.animate()
+                            .alpha(0)
+                            .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
+                            .setDuration(300)
+                            .setStartDelay(0)
+                            .withEndAction(() -> {
+                                mBackdrop.setVisibility(View.GONE);
+                                mBackdropFront.animate().cancel();
+                                mBackdropBack.setImageDrawable(null);
+                                mHandler.post(mHideBackdropFront);
+                            });
+                    if (mKeyguardMonitor.isKeyguardFadingAway()) {
+                        mBackdrop.animate()
+                                // Make it disappear faster, as the focus should be on the activity
+                                // behind.
+                                .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
+                                .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
+                                .setInterpolator(Interpolators.LINEAR)
+                                .start();
+                    }
+                }
+            }
+        }
+        Trace.endSection();
+    }
+
+    public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
+            ScrimController scrimController, LockscreenWallpaper lockscreenWallpaper) {
+        mBackdrop = backdrop;
+        mBackdropFront = backdropFront;
+        mBackdropBack = backdropBack;
+        mScrimController = scrimController;
+        mLockscreenWallpaper = lockscreenWallpaper;
+    }
+
+    public void setBiometricUnlockController(BiometricUnlockController biometricUnlockController) {
+        mBiometricUnlockController = biometricUnlockController;
+    }
+
+    /**
+     * Hide the album artwork that is fading out and release its bitmap.
+     */
+    protected final Runnable mHideBackdropFront = new Runnable() {
+        @Override
+        public void run() {
+            if (DEBUG_MEDIA) {
+                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
+            }
+            mBackdropFront.setVisibility(View.INVISIBLE);
+            mBackdropFront.animate().cancel();
+            mBackdropFront.setImageDrawable(null);
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index c58eb80..5c8f4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -19,6 +19,7 @@
 import android.os.Handler;
 import android.view.View;
 
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -31,9 +32,7 @@
  * for affecting the state of the system (e.g. starting an intent, given that the presenter may
  * want to perform some action before doing so).
  */
-public interface NotificationPresenter extends NotificationData.Environment,
-        NotificationRemoteInputManager.Callback,
-        ExpandableNotificationRow.OnExpandClickListener,
+public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener,
         ActivatableNotificationView.OnActivatedListener,
         NotificationEntryManager.Callback {
     /**
@@ -43,59 +42,23 @@
     boolean isPresenterFullyCollapsed();
 
     /**
-     * Returns true if the presenter is locked. For example, if the keyguard is active.
-     */
-    boolean isPresenterLocked();
-
-    /**
      * Runs the given intent. The presenter may want to run some animations or close itself when
      * this happens.
      */
     void startNotificationGutsIntent(Intent intent, int appUid, ExpandableNotificationRow row);
 
     /**
-     * Returns the Handler for NotificationPresenter.
-     */
-    Handler getHandler();
-
-    /**
      * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
      */
     void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
 
     /**
-     * Called when the locked status of the device is changed for a work profile.
-     */
-    void onWorkChallengeChanged();
-
-    /**
      * Called when the current user changes.
      * @param newUserId new user id
      */
     void onUserSwitched(int newUserId);
 
     /**
-     * Gets the NotificationLockscreenUserManager for this Presenter.
-     */
-    NotificationLockscreenUserManager getNotificationLockscreenUserManager();
-
-    /**
-     * Wakes the device up if dozing.
-     *
-     * @param time the time when the request to wake up was issued
-     * @param where which view caused this wake up request
-     */
-    void wakeUpIfDozing(long time, View where);
-
-    /**
-     * True if the device currently requires a PIN, pattern, or password to unlock.
-     *
-     * @param userId user id to query about
-     * @return true iff the device is locked
-     */
-    boolean isDeviceLocked(int userId);
-
-    /**
      * @return true iff the device is in vr mode
      */
     boolean isDeviceInVrMode();
@@ -114,7 +77,36 @@
     int getMaxNotificationsWhileLocked(boolean recompute);
 
     /**
-     * Called when the row states are updated by NotificationViewHierarchyManager.
+     * True if the presenter
+     * @return
+     */
+    default boolean isPresenterLocked() { return false; }
+
+    /**
+     * Called when the row states are updated by {@link NotificationViewHierarchyManager}.
      */
     void onUpdateRowStates();
+
+    /**
+     * @return true if the shade is collapsing.
+     */
+    boolean isCollapsing();
+
+    /**
+     * @return true if the shade is collapsing to show an activity over the lock screen
+     */
+    default public boolean isCollapsingToShowActivityOverLockscreen() {
+        return false;
+    }
+
+    /**
+     * Get the {@link ActivityLaunchAnimator} from the presenter so it can be queried by
+     * {@link com.android.systemui.statusbar.phone.StatusBar}
+     * @return the current animator
+     * @deprecated This is only here for now because StatusBar is still the ActivityLaunchAnimator
+     * callback but shouldn't be.
+     */
+    default public ActivityLaunchAnimator getActivityLaunchAnimator() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index ea7e03e..f30377e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -46,9 +47,11 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
+import com.android.systemui.InitController;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
 import java.io.FileDescriptor;
@@ -97,13 +100,18 @@
             Dependency.get(NotificationLockscreenUserManager.class);
     protected final SmartReplyController mSmartReplyController =
             Dependency.get(SmartReplyController.class);
+    private final NotificationEntryManager mEntryManager
+            = Dependency.get(NotificationEntryManager.class);
+
+    // Lazy
+    private ShadeController mShadeController;
 
     protected final Context mContext;
     private final UserManager mUserManager;
+    private final KeyguardManager mKeyguardManager;
 
     protected RemoteInputController mRemoteInputController;
     protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
     protected NotificationLifetimeExtender.NotificationSafeToRemoveCallback
             mNotificationLifetimeFinishedCallback;
     protected IStatusBarService mBarService;
@@ -115,7 +123,7 @@
         @Override
         public boolean onClickHandler(
                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
-            mPresenter.wakeUpIfDozing(SystemClock.uptimeMillis(), view);
+            getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), view);
 
             if (handleRemoteInput(view, pendingIntent)) {
                 return true;
@@ -240,7 +248,7 @@
                     return true;
                 }
                 if (mUserManager.getUserInfo(userId).isManagedProfile()
-                        && mPresenter.isDeviceLocked(userId)) {
+                        && mKeyguardManager.isDeviceLocked(userId)) {
                     mCallback.onLockedWorkRemoteInput(userId, row, view);
                     return true;
                 }
@@ -291,20 +299,26 @@
         }
     };
 
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
     public NotificationRemoteInputManager(Context context) {
         mContext = context;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         addLifetimeExtenders();
+        mKeyguardManager = context.getSystemService(KeyguardManager.class);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager,
             Callback callback,
             RemoteInputController.Delegate delegate) {
         mPresenter = presenter;
-        mEntryManager = entryManager;
         mCallback = callback;
         mRemoteInputController = new RemoteInputController(delegate);
         mRemoteInputController.addCallback(new RemoteInputController.Callback() {
@@ -318,7 +332,7 @@
                     // view it is already canceled, so we'll need to cancel it on the apps behalf
                     // after sending - unless the app posts an update in the mean time, so wait a
                     // bit.
-                    mPresenter.getHandler().postDelayed(() -> {
+                    Dependency.get(Dependency.MAIN_HANDLER).postDelayed(() -> {
                         if (mEntriesKeptForRemoteInputActive.remove(entry)) {
                             mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7fa0426..cd3da123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -34,6 +34,7 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -103,7 +104,8 @@
     }
 
     @Override
-    protected void onFinishInflate() {
+    @VisibleForTesting
+    public void onFinishInflate() {
         super.onFinishInflate();
         mShelfIcons = findViewById(R.id.content);
         mShelfIcons.setClipChildren(false);
@@ -793,7 +795,7 @@
     private void setOpenedAmount(float openedAmount) {
         mNoAnimationsInThisFrame = openedAmount == 1.0f && mOpenedAmount == 0.0f;
         mOpenedAmount = openedAmount;
-        if (!mAmbientState.isPanelFullWidth()) {
+        if (!mAmbientState.isPanelFullWidth() || mAmbientState.isDark()) {
             // We don't do a transformation at all, lets just assume we are fully opened
             openedAmount = 1.0f;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index f69ad43..92765bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -24,6 +24,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -31,6 +32,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -57,6 +59,13 @@
             Dependency.get(NotificationGroupManager.class);
     protected final VisualStabilityManager mVisualStabilityManager =
             Dependency.get(VisualStabilityManager.class);
+    private final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
+
+    // Lazy
+    private ShadeController mShadeController;
 
     /**
      * {@code true} if notifications not part of a group should by default be rendered in their
@@ -66,9 +75,15 @@
     private final boolean mAlwaysExpandNonGroupedNotification;
 
     private NotificationPresenter mPresenter;
-    private NotificationEntryManager mEntryManager;
     private NotificationListContainer mListContainer;
 
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
     public NotificationViewHierarchyManager(Context context) {
         Resources res = context.getResources();
         mAlwaysExpandNonGroupedNotification =
@@ -76,9 +91,8 @@
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager, NotificationListContainer listContainer) {
+            NotificationListContainer listContainer) {
         mPresenter = presenter;
-        mEntryManager = entryManager;
         mListContainer = listContainer;
     }
 
@@ -291,9 +305,9 @@
         final int N = mListContainer.getContainerChildCount();
 
         int visibleNotifications = 0;
-        boolean isLocked = mPresenter.isPresenterLocked();
+        boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
         int maxNotifications = -1;
-        if (isLocked) {
+        if (onKeyguard) {
             maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
         }
         mListContainer.setMaxDisplayedNotifications(maxNotifications);
@@ -311,9 +325,9 @@
             boolean isChildNotification =
                     mGroupManager.isChildInGroupWithSummary(entry.notification);
 
-            row.setOnKeyguard(isLocked);
+            row.setOnKeyguard(onKeyguard);
 
-            if (!isLocked) {
+            if (!onKeyguard) {
                 // If mAlwaysExpandNonGroupedNotification is false, then only expand the
                 // very first notification and if it's not a child of grouped notifications.
                 row.setSystemExpanded(mAlwaysExpandNonGroupedNotification
@@ -321,15 +335,26 @@
                         && !row.isLowPriority()));
             }
 
-            entry.row.setOnAmbient(mPresenter.isDozing());
+            entry.row.setOnAmbient(getShadeController().isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
             boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
                     .notification);
+            if (!showOnKeyguard) {
+                // min priority notifications should show if their summary is showing
+                if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
+                    ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+                            entry.notification);
+                    if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
+                            summary.getStatusBarNotification()))         {
+                        showOnKeyguard = true;
+                    }
+                }
+            }
             if (suppressedSummary
                     || mLockscreenUserManager.shouldHideNotifications(userId)
-                    || (isLocked && !showOnKeyguard)) {
+                    || (onKeyguard && !showOnKeyguard)) {
                 entry.row.setVisibility(View.GONE);
             } else {
                 boolean wasGone = entry.row.getVisibility() == View.GONE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index cc5fbe5..1e04377 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -38,7 +38,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import androidx.core.graphics.ColorUtils;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -49,6 +48,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
 
+import androidx.core.graphics.ColorUtils;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Interpolators;
@@ -121,6 +122,7 @@
     private StatusBarNotification mNotification;
     private final boolean mBlocked;
     private int mDensity;
+    private boolean mNightMode;
     private float mIconScale = 1.0f;
     private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private float mDotRadius;
@@ -171,10 +173,10 @@
         setNotification(sbn);
         setScaleType(ScaleType.CENTER);
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
-        if (mNotification != null) {
-            setDecorColor(getContext().getColor(
-                    com.android.internal.R.color.notification_default_color_light));
-        }
+        Configuration configuration = context.getResources().getConfiguration();
+        mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES;
+        initializeDecorColor();
         reloadDimens();
         maybeUpdateIconScaleDimens();
     }
@@ -222,6 +224,12 @@
             maybeUpdateIconScaleDimens();
             updateDrawable();
         }
+        boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES;
+        if (nightMode != mNightMode) {
+            mNightMode = nightMode;
+            initializeDecorColor();
+        }
     }
 
     private void reloadDimens() {
@@ -540,6 +548,14 @@
         updateDecorColor();
     }
 
+    private void initializeDecorColor() {
+        if (mNotification != null) {
+            setDecorColor(getContext().getColor(mNightMode
+                    ? com.android.internal.R.color.notification_default_color_dark
+                    : com.android.internal.R.color.notification_default_color_light));
+        }
+    }
+
     private void updateDecorColor() {
         int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount);
         if (mDotPaint.getColor() != color) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index d6719f0..12c0fcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -22,6 +22,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.statusbar.phone.StatusBar;
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -39,9 +40,11 @@
             = (o1, o2) -> Integer.compare(o1.rank, o2.rank);
 
     private final ArrayList<RankedListener> mListeners = new ArrayList<>();
+    private boolean mIsDozing;
     private int mState;
     private int mLastState;
     private boolean mLeaveOpenOnKeyguardHide;
+    private boolean mKeyguardRequested;
 
     // TODO: b/115739177 (remove this explicit ordering if we can)
     @Retention(SOURCE)
@@ -57,6 +60,11 @@
         return mState;
     }
 
+    /**
+     * Update the status bar state
+     * @param state see {@link StatusBarState} for valid options
+     * @return {@code true} if the state changed, else {@code false}
+     */
     public boolean setState(int state) {
         if (state > MAX_STATE || state < MIN_STATE) {
             throw new IllegalArgumentException("Invalid state " + state);
@@ -82,6 +90,32 @@
         return true;
     }
 
+    public boolean isDozing() {
+        return mIsDozing;
+    }
+
+    /**
+     * Update the dozing state from {@link StatusBar}'s perspective
+     * @param isDozing well, are we dozing?
+     * @return {@code true} if the state changed, else {@code false}
+     */
+    @SuppressWarnings("UnusedReturnValue")
+    public boolean setIsDozing(boolean isDozing) {
+        if (mIsDozing == isDozing) {
+            return false;
+        }
+
+        mIsDozing = isDozing;
+
+        synchronized (mListeners) {
+            for (RankedListener rl : new ArrayList<>(mListeners)) {
+                rl.listener.onDozingChanged(isDozing);
+            }
+        }
+
+        return true;
+    }
+
     public boolean goingToFullShade() {
         return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
     }
@@ -140,18 +174,16 @@
         }
     }
 
-    public static String describe(int state) {
-        return StatusBarState.toShortString(state);
+    public void setKeyguardRequested(boolean keyguardRequested) {
+        mKeyguardRequested = keyguardRequested;
     }
 
-    public interface StateListener {
-        public default void onStatePreChange(int oldState, int newState) {
-        }
+    public boolean isKeyguardRequested() {
+        return mKeyguardRequested;
+    }
 
-        public default void onStatePostChange() {
-        }
-
-        public void onStateChanged(int newState);
+    public static String describe(int state) {
+        return StatusBarState.toShortString(state);
     }
 
     private class RankedListener {
@@ -163,4 +195,40 @@
             rank = r;
         }
     }
+
+    /**
+     * Listener for StatusBarState updates
+     */
+    public interface StateListener {
+
+        /**
+         * Callback before the new state is applied, for those who need to preempt the change
+         * @param oldState state before the change
+         * @param newState new state to be applied in {@link #onStateChanged}
+         */
+        public default void onStatePreChange(int oldState, int newState) {
+        }
+
+        /**
+         * Callback after all listeners have had a chance to update based on the state change
+         */
+        public default void onStatePostChange() {
+        }
+
+        /**
+         * Required callback. Get the new state and do what you will with it. Keep in mind that
+         * other listeners are typically unordered and don't rely on your work being done before
+         * other peers
+         *
+         * Only called if the state is actually different
+         * @param newState the new {@link StatusBarState}
+         */
+        public void onStateChanged(int newState);
+
+        /**
+         * Callback to be notified when Dozing changes. Dozing is stored separately from state.
+         * @param isDozing {@code true} if dozing according to {@link StatusBar}
+         */
+        public default void onDozingChanged(boolean isDozing) {}
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 7be0eab..c35e1da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -552,12 +552,6 @@
     }
 
     @Override
-    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
-        // Do nothing, we don't want to display media art in the lock screen for a car.
-    }
-
-
-    @Override
     public void animateExpandNotificationsPanel() {
         // Because space is usually constrained in the auto use-case, there should not be a
         // pinned notification when the shade has been expanded. Ensure this by removing all heads-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
index 8cae806..9e99fbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AppOpsListener.java
@@ -33,10 +33,11 @@
     // Dependencies:
     private final ForegroundServiceController mFsc =
             Dependency.get(ForegroundServiceController.class);
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
 
     private final Context mContext;
     protected NotificationPresenter mPresenter;
-    protected NotificationEntryManager mEntryManager;
     protected final AppOpsManager mAppOps;
 
     protected static final int[] OPS = new int[] {AppOpsManager.OP_CAMERA,
@@ -48,10 +49,8 @@
         mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
     }
 
-    public void setUpWithPresenter(NotificationPresenter presenter,
-            NotificationEntryManager entryManager) {
+    public void setUpWithPresenter(NotificationPresenter presenter) {
         mPresenter = presenter;
-        mEntryManager = entryManager;
         mAppOps.startWatchingActive(OPS, this);
     }
 
@@ -62,7 +61,7 @@
     @Override
     public void onOpActiveChanged(int code, int uid, String packageName, boolean active) {
         mFsc.onAppOpChanged(code, uid, packageName, active);
-        mPresenter.getHandler().post(() -> {
+        Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
           mEntryManager.updateNotificationsForAppOp(code, uid, packageName, active);
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index d097c8e..3539fff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -50,9 +50,6 @@
 import android.util.ArraySet;
 import android.view.View;
 import android.widget.ImageView;
-import android.widget.RemoteViews;
-
-import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -60,13 +57,16 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.InitController;
 import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.ZenModeController;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -75,16 +75,23 @@
 import java.util.List;
 import java.util.Objects;
 
+import androidx.annotation.Nullable;
+
 /**
  * The list of currently displaying notifications.
  */
 public class NotificationData {
 
-    private final Environment mEnvironment;
-    private HeadsUpManager mHeadsUpManager;
+    /**
+     * These dependencies are late init-ed
+     */
+    private KeyguardEnvironment mEnvironment;
+    private ShadeController mShadeController;
+    private NotificationMediaManager mMediaManager;
+    private ForegroundServiceController mFsc;
+    private NotificationLockscreenUserManager mUserManager;
 
-    final ZenModeController mZen = Dependency.get(ZenModeController.class);
-    final ForegroundServiceController mFsc = Dependency.get(ForegroundServiceController.class);
+    private HeadsUpManager mHeadsUpManager;
 
     public static final class Entry {
         private static final long LAUNCH_COOLDOWN = 2000;
@@ -102,11 +109,6 @@
         public boolean autoRedacted; // whether the redacted notification was generated by us
         public int targetSdk;
         private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
-        public RemoteViews cachedContentView;
-        public RemoteViews cachedBigContentView;
-        public RemoteViews cachedHeadsUpContentView;
-        public RemoteViews cachedPublicContentView;
-        public RemoteViews cachedAmbientContentView;
         public CharSequence remoteInputText;
         public List<SnoozeCriterion> snoozeCriteria;
         public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
@@ -178,14 +180,6 @@
             }
         }
 
-        public View getExpandedContentView() {
-            return row.getPrivateLayout().getExpandedChild();
-        }
-
-        public View getPublicContentView() {
-            return row.getPublicLayout().getContractedChild();
-        }
-
         public void notifyFullScreenIntentLaunched() {
             setInterruption();
             lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -389,7 +383,8 @@
     private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
     private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
 
-    private NotificationGroupManager mGroupManager;
+    private final NotificationGroupManager mGroupManager
+            = Dependency.get(NotificationGroupManager.class);
 
     private RankingMap mRankingMap;
     private final Ranking mTmpRanking = new Ranking();
@@ -421,7 +416,7 @@
                 bRank = mRankingB.getRank();
             }
 
-            String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();
+            String mediaNotification = getMediaManager().getMediaNotificationKey();
 
             // IMPORTANCE_MIN media streams are allowed to drift to the bottom
             final boolean aMedia = a.key.equals(mediaNotification)
@@ -456,13 +451,43 @@
         }
     };
 
-    public NotificationData(Environment environment) {
-        mEnvironment = environment;
-        mGroupManager = environment.getGroupManager();
+    private KeyguardEnvironment getEnvironment() {
+        if (mEnvironment == null) {
+            mEnvironment = Dependency.get(KeyguardEnvironment.class);
+        }
+        return mEnvironment;
+    }
+
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
+    private NotificationMediaManager getMediaManager() {
+        if (mMediaManager == null) {
+            mMediaManager = Dependency.get(NotificationMediaManager.class);
+        }
+        return mMediaManager;
+    }
+
+    private ForegroundServiceController getFsc() {
+        if (mFsc == null) {
+            mFsc = Dependency.get(ForegroundServiceController.class);
+        }
+        return mFsc;
+    }
+
+    private NotificationLockscreenUserManager getUserManager() {
+        if (mUserManager == null) {
+            mUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+        }
+        return mUserManager;
     }
 
     /**
-     * Returns the sorted list of active notifications (depending on {@link Environment}
+     * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
      *
      * <p>
      * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
@@ -482,7 +507,7 @@
             for (int i = 0; i < N; i++) {
                 Entry entry = mEntries.valueAt(i);
                 final StatusBarNotification sbn = entry.notification;
-                if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
+                if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
                     continue;
                 }
                 mFilteredForUser.add(entry);
@@ -733,27 +758,27 @@
      */
     public boolean shouldFilterOut(Entry entry) {
         final StatusBarNotification sbn = entry.notification;
-        if (!(mEnvironment.isDeviceProvisioned() ||
+        if (!(getEnvironment().isDeviceProvisioned() ||
                 showNotificationEvenIfUnprovisioned(sbn))) {
             return true;
         }
 
-        if (!mEnvironment.isNotificationForCurrentProfiles(sbn)) {
+        if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
             return true;
         }
 
-        if (mEnvironment.isSecurelyLocked(sbn.getUserId()) &&
+        if (getUserManager().isLockscreenPublicMode(sbn.getUserId()) &&
                 (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
-                        || mEnvironment.shouldHideNotifications(sbn.getUserId())
-                        || mEnvironment.shouldHideNotifications(sbn.getKey()))) {
+                        || getUserManager().shouldHideNotifications(sbn.getUserId())
+                        || getUserManager().shouldHideNotifications(sbn.getKey()))) {
             return true;
         }
 
-        if (mEnvironment.isDozing() && shouldSuppressAmbient(entry)) {
+        if (getShadeController().isDozing() && shouldSuppressAmbient(entry)) {
             return true;
         }
 
-        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(entry)) {
+        if (!getShadeController().isDozing() && shouldSuppressNotificationList(entry)) {
             return true;
         }
 
@@ -766,15 +791,16 @@
             return true;
         }
 
-        if (mFsc.isDungeonNotification(sbn) && !mFsc.isDungeonNeededForUser(sbn.getUserId())) {
+        if (getFsc().isDungeonNotification(sbn)
+                && !getFsc().isDungeonNeededForUser(sbn.getUserId())) {
             // this is a foreground-service disclosure for a user that does not need to show one
             return true;
         }
-        if (mFsc.isSystemAlertNotification(sbn)) {
+        if (getFsc().isSystemAlertNotification(sbn)) {
             final String[] apps = sbn.getNotification().extras.getStringArray(
                     Notification.EXTRA_FOREGROUND_APPS);
             if (apps != null && apps.length >= 1) {
-                if (!mFsc.isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+                if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
                     return true;
                 }
             }
@@ -852,18 +878,8 @@
     /**
      * Provides access to keyguard state and user settings dependent data.
      */
-    public interface Environment {
-        public boolean isSecurelyLocked(int userId);
-        public boolean shouldHideNotifications(int userid);
-        public boolean shouldHideNotifications(String key);
-        public boolean isDeviceProvisioned();
-        public boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
-        public String getCurrentMediaNotificationKey();
-        public NotificationGroupManager getGroupManager();
-
-        /**
-         * @return true iff the device is dozing
-         */
-        boolean isDozing();
+    public interface KeyguardEnvironment {
+        boolean isDeviceProvisioned();
+        boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a3e982e..3bea7db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -16,8 +16,9 @@
 package com.android.systemui.statusbar.notification;
 
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager
-        .FORCE_REMOTE_INPUT_HISTORY;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -58,11 +59,12 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.UiOffloadThread;
-import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -70,12 +72,15 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationUiAdjustment;
 import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
-import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -105,33 +110,31 @@
     protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
     protected final NotificationClicker mNotificationClicker = new NotificationClicker();
 
-    // Dependencies:
-    protected final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    protected final NotificationGroupManager mGroupManager =
+    private final NotificationGroupManager mGroupManager =
             Dependency.get(NotificationGroupManager.class);
-    protected final NotificationGutsManager mGutsManager =
+    private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
-    protected final NotificationRemoteInputManager mRemoteInputManager =
-            Dependency.get(NotificationRemoteInputManager.class);
-    protected final NotificationMediaManager mMediaManager =
-            Dependency.get(NotificationMediaManager.class);
-    protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
-    protected final DeviceProvisionedController mDeviceProvisionedController =
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final DeviceProvisionedController mDeviceProvisionedController =
             Dependency.get(DeviceProvisionedController.class);
-    protected final VisualStabilityManager mVisualStabilityManager =
+    private final VisualStabilityManager mVisualStabilityManager =
             Dependency.get(VisualStabilityManager.class);
-    protected final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
-    protected final ForegroundServiceController mForegroundServiceController =
+    private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+    private final ForegroundServiceController mForegroundServiceController =
             Dependency.get(ForegroundServiceController.class);
-    protected final NotificationListener mNotificationListener =
-            Dependency.get(NotificationListener.class);
-    protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+    private final AmbientPulseManager mAmbientPulseManager =
+            Dependency.get(AmbientPulseManager.class);
+
+    // Lazily retrieved dependencies
+    private NotificationRemoteInputManager mRemoteInputManager;
+    private NotificationMediaManager mMediaManager;
+    private NotificationListener mNotificationListener;
+    private ShadeController mShadeController;
 
     protected IDreamManager mDreamManager;
     protected IStatusBarService mBarService;
-    protected NotificationPresenter mPresenter;
-    protected Callback mCallback;
+    private NotificationPresenter mPresenter;
+    private Callback mCallback;
     protected PowerManager mPowerManager;
     protected NotificationListenerService.RankingMap mLatestRankingMap;
     protected HeadsUpManager mHeadsUpManager;
@@ -144,7 +147,6 @@
             = new ArrayList<>();
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
 
-
     private final class NotificationClicker implements View.OnClickListener {
 
         @Override
@@ -154,7 +156,7 @@
                 return;
             }
 
-            mPresenter.wakeUpIfDozing(SystemClock.uptimeMillis(), v);
+            getShadeController().wakeUpIfDozing(SystemClock.uptimeMillis(), v);
 
             final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
             final StatusBarNotification sbn = row.getStatusBarNotification();
@@ -227,20 +229,55 @@
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
         mMessagingUtil = new NotificationMessagingUtil(context);
+        mNotificationData = new NotificationData();
+        Dependency.get(InitController.class).addPostInitTask(this::onPostInit);
+    }
+
+    private void onPostInit() {
         mGroupManager.setPendingEntries(mPendingNotifications);
     }
 
+    /**
+     * Our dependencies can have cyclic references, so some need to be lazy
+     */
+    private NotificationRemoteInputManager getRemoteInputManager() {
+        if (mRemoteInputManager == null) {
+            mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
+        }
+        return mRemoteInputManager;
+    }
+
+    private NotificationMediaManager getMediaManager() {
+        if (mMediaManager == null) {
+            mMediaManager = Dependency.get(NotificationMediaManager.class);
+        }
+        return mMediaManager;
+    }
+
+    private NotificationListener getNotificationListener() {
+        if (mNotificationListener == null) {
+            mNotificationListener = Dependency.get(NotificationListener.class);
+        }
+        return mNotificationListener;
+    }
+
+    private ShadeController getShadeController() {
+        if (mShadeController == null) {
+            mShadeController = Dependency.get(ShadeController.class);
+        }
+        return mShadeController;
+    }
+
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer, Callback callback,
             HeadsUpManager headsUpManager) {
         mPresenter = presenter;
         mCallback = callback;
-        mNotificationData = new NotificationData(presenter);
         mHeadsUpManager = headsUpManager;
         mNotificationData.setHeadsUpManager(mHeadsUpManager);
         mListContainer = listContainer;
 
-        mHeadsUpObserver = new ContentObserver(mPresenter.getHandler()) {
+        mHeadsUpObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
             @Override
             public void onChange(boolean selfChange) {
                 boolean wasUsing = mUseHeadsUp;
@@ -273,7 +310,7 @@
         mNotificationLifetimeExtenders.add(mHeadsUpManager);
         mNotificationLifetimeExtenders.add(mAmbientPulseManager);
         mNotificationLifetimeExtenders.add(mGutsManager);
-        mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders());
+        mNotificationLifetimeExtenders.addAll(getRemoteInputManager().getLifetimeExtenders());
 
         for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
             extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
@@ -289,6 +326,14 @@
         return mNotificationData;
     }
 
+    protected Context getContext() {
+        return mContext;
+    }
+
+    protected NotificationPresenter getPresenter() {
+        return mPresenter;
+    }
+
     public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
         return mGutsManager::openGuts;
     }
@@ -343,7 +388,7 @@
         row.setInflationCallback(this);
         row.setLongPressListener(getNotificationLongClicker());
         mListContainer.bindRow(row);
-        mRemoteInputManager.bindRow(row);
+        getRemoteInputManager().bindRow(row);
 
         // Get the app name.
         // Note that Notification.Builder#bindHeaderAppName has similar logic
@@ -382,7 +427,7 @@
                 true);
         NotificationData.Entry entry = mNotificationData.get(n.getKey());
 
-        mRemoteInputManager.onPerformRemoveNotification(n, entry);
+        getRemoteInputManager().onPerformRemoveNotification(n, entry);
         final String pkg = n.getPackageName();
         final String tag = n.getTag();
         final int id = n.getId();
@@ -440,25 +485,48 @@
     }
 
     private void addEntry(NotificationData.Entry shadeEntry) {
-        if (shouldHeadsUp(shadeEntry)) {
-            mHeadsUpManager.showNotification(shadeEntry);
-            // Mark as seen immediately
-            setNotificationShown(shadeEntry.notification);
-        }
-        if (shouldPulse(shadeEntry)) {
-            mAmbientPulseManager.showNotification(shadeEntry);
-        }
         addNotificationViews(shadeEntry);
         mCallback.onNotificationAdded(shadeEntry);
     }
 
+    /**
+     * Adds the entry to the respective alerting manager if the content view was inflated and
+     * the entry should still alert.
+     *
+     * @param entry entry to add
+     * @param inflatedFlags flags representing content views that were inflated
+     */
+    private void showAlertingView(NotificationData.Entry entry,
+            @InflationFlag int inflatedFlags) {
+        if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
+            // Possible for shouldHeadsUp to change between the inflation starting and ending.
+            // If it does and we no longer need to heads up, we should free the view.
+            if (shouldHeadsUp(entry)) {
+                mHeadsUpManager.showNotification(entry);
+                // Mark as seen immediately
+                setNotificationShown(entry.notification);
+            } else {
+                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+            }
+        }
+        if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
+            if (shouldPulse(entry)) {
+                mAmbientPulseManager.showNotification(entry);
+            } else {
+                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+            }
+        }
+    }
+
     @Override
-    public void onAsyncInflationFinished(NotificationData.Entry entry) {
+    public void onAsyncInflationFinished(NotificationData.Entry entry,
+            @InflationFlag int inflatedFlags) {
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
         // the list, otherwise we might get leaks.
         boolean isNew = mNotificationData.get(entry.key) == null;
         if (isNew && !entry.row.isRemoved()) {
+            showAlertingView(entry, inflatedFlags);
             addEntry(entry);
         } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
             mVisualStabilityManager.onLowPriorityUpdated(entry);
@@ -484,7 +552,7 @@
             // sending look longer than it takes.
             // Also we should not defer the removal if reordering isn't allowed since otherwise
             // some notifications can't disappear before the panel is closed.
-            boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
+            boolean ignoreEarliestRemovalTime = getRemoteInputManager().getController().isSpinning(key)
                     && !FORCE_REMOTE_INPUT_HISTORY
                     || !mVisualStabilityManager.isReorderingAllowed();
             mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
@@ -519,7 +587,7 @@
             extender.setShouldManageLifetime(entry, false /* shouldManage */);
         }
 
-        mMediaManager.onNotificationRemoved(key);
+        getMediaManager().onNotificationRemoved(key);
         mForegroundServiceController.removeNotification(entry.notification);
 
         if (entry.row != null) {
@@ -574,8 +642,8 @@
                 boolean isForeground = (row.getStatusBarNotification().getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
-                        mRemoteInputManager.shouldKeepForRemoteInputHistory(childEntry)
-                        || mRemoteInputManager.shouldKeepForSmartReplyHistory(childEntry);
+                        getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
+                        || getRemoteInputManager().shouldKeepForSmartReplyHistory(childEntry);
                 if (isForeground || keepForReply) {
                     // the child is a foreground service notification which we can't remove or it's
                     // a child we're keeping around for reply!
@@ -605,7 +673,6 @@
 
     protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
-        row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
         boolean isUpdate = mNotificationData.get(entry.key) != null;
         boolean wasLowPriority = row.isLowPriority();
@@ -636,7 +703,13 @@
         row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
         row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
         row.setSmartActions(entry.smartActions);
-        row.updateNotification(entry);
+        row.setEntry(entry);
+
+        row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
+        row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, shouldPulse(entry));
+        row.setNeedsRedaction(
+                Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
+        row.inflateViews();
     }
 
     protected void addNotificationViews(NotificationData.Entry entry) {
@@ -786,7 +859,7 @@
                 mNotificationData.getImportance(key));
 
         boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
-        if (mPresenter.isDozing()) {
+        if (getShadeController().isDozing()) {
             updateAlertState(entry, shouldPulse(entry), alertAgain, mAmbientPulseManager);
         } else {
             updateAlertState(entry, shouldHeadsUp(entry), alertAgain, mHeadsUpManager);
@@ -801,7 +874,8 @@
 
         if (DEBUG) {
             // Is this for you?
-            boolean isForCurrentUser = mPresenter.isNotificationForCurrentProfiles(notification);
+            boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
+                    .isNotificationForCurrentProfiles(notification);
             Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
         }
 
@@ -885,7 +959,7 @@
     public boolean shouldHeadsUp(NotificationData.Entry entry) {
         StatusBarNotification sbn = entry.notification;
 
-        if (mPresenter.isDozing()) {
+        if (getShadeController().isDozing()) {
             if (DEBUG) {
                 Log.d(TAG, "No heads up: device is dozing: " + sbn.getKey());
             }
@@ -966,7 +1040,7 @@
     protected boolean shouldPulse(NotificationData.Entry entry) {
         StatusBarNotification sbn = entry.notification;
 
-        if (!mPresenter.isDozing()) {
+        if (!getShadeController().isDozing()) {
             if (DEBUG) {
                 Log.d(TAG, "No pulsing: not dozing: " + sbn.getKey());
             }
@@ -1044,7 +1118,7 @@
 
     protected void setNotificationsShown(String[] keys) {
         try {
-            mNotificationListener.setNotificationsShown(keys);
+            getNotificationListener().setNotificationsShown(keys);
         } catch (RuntimeException e) {
             Log.d(TAG, "failed setNotificationsShown: ", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 81208c4..53ebe74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -24,7 +24,10 @@
 public interface VisibilityLocationProvider {
 
     /**
-     * @return whether the view is in a visible location right now.
+     * Returns whether an ExpandableNotificationRow is in a visible location or not.
+     *
+     * @param row
+     * @return true if row is in a visible location
      */
     boolean isInVisibleLocation(ExpandableNotificationRow row);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index e96e176..b5fbde1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -56,8 +56,9 @@
     private final NotificationListenerService mNotificationListener =
             Dependency.get(NotificationListener.class);
     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
+    protected NotificationEntryManager mEntryManager
+            = Dependency.get(NotificationEntryManager.class);
 
-    protected NotificationEntryManager mEntryManager;
     protected Handler mHandler = new Handler();
     protected IStatusBarService mBarService;
     private long mLastVisibilityReportUptimeMs;
@@ -147,9 +148,7 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
-    public void setUpWithEntryManager(NotificationEntryManager entryManager,
-            NotificationListContainer listContainer) {
-        mEntryManager = entryManager;
+    public void setUpWithContainer(NotificationListContainer listContainer) {
         mListContainer = listContainer;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index bce613a..22c37fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,12 +17,19 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -38,8 +45,8 @@
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Build;
-import android.os.SystemClock;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.AttributeSet;
@@ -72,28 +79,31 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.notification.logging.NotificationCounters;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
 import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.BooleanSupplier;
@@ -203,6 +213,11 @@
      */
     private boolean mIsAmbientPulsing;
 
+    /**
+     * Whether or not the notification should be redacted on the lock screen, i.e has sensitive
+     * content which should be redacted on the lock screen.
+     */
+    private boolean mNeedsRedaction;
     private boolean mLastChronometerRunning = true;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
@@ -429,15 +444,65 @@
         }
     }
 
-    public void updateNotification(NotificationData.Entry entry) {
+    /**
+     * Set the entry for the row.
+     *
+     * @param entry the entry this row is tied to
+     */
+    public void setEntry(@NonNull NotificationData.Entry entry) {
         mEntry = entry;
         mStatusBarNotification = entry.notification;
-        mNotificationInflater.inflateNotificationViews();
-
         cacheIsSystemNotification();
     }
 
     /**
+     * Inflate views based off the inflation flags set.  Inflation happens asynchronously.
+     */
+    public void inflateViews() {
+        mNotificationInflater.inflateNotificationViews();
+    }
+
+    /**
+     * Marks a content view as freeable, setting it so that future inflations do not reinflate
+     * and ensuring that the view is freed when it is safe to remove.
+     *
+     * @param inflationFlag flag corresponding to the content view to be freed
+     */
+    public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+        // View should not be reinflated in the future
+        updateInflationFlag(inflationFlag, false);
+        Runnable freeViewRunnable = () ->
+                mNotificationInflater.freeNotificationView(inflationFlag);
+        switch (inflationFlag) {
+            case FLAG_CONTENT_VIEW_HEADS_UP:
+                getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
+                        freeViewRunnable);
+                break;
+            case FLAG_CONTENT_VIEW_AMBIENT:
+                getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT,
+                        freeViewRunnable);
+                getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT,
+                        freeViewRunnable);
+                break;
+            case FLAG_CONTENT_VIEW_PUBLIC:
+                getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED,
+                        freeViewRunnable);
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Update whether or not a content view should be inflated.
+     *
+     * @param flag the flag corresponding to the content view
+     * @param shouldInflate true if it should be inflated, false if it should not
+     */
+    public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
+        mNotificationInflater.updateInflationFlag(flag, shouldInflate);
+    }
+
+    /**
      * Caches whether or not this row contains a system notification. Note, this is only cached
      * once per notification as the packageInfo can't technically change for a notification row.
      */
@@ -499,6 +564,9 @@
         if (mIconAnimationRunning) {
             setIconAnimationRunning(true);
         }
+        if (mLastChronometerRunning) {
+            setChronometerRunning(true);
+        }
         if (mNotificationParent != null) {
             mNotificationParent.updateChildrenHeaderAppearance();
         }
@@ -557,7 +625,8 @@
     }
 
     private void updateLimitsForView(NotificationContentView layout) {
-        boolean customView = layout.getContractedChild().getId()
+        boolean customView = layout.getContractedChild() != null
+                && layout.getContractedChild().getId()
                 != com.android.internal.R.id.status_bar_latest_event_content;
         boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
         boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
@@ -581,7 +650,7 @@
             headsUpHeight = mMaxHeadsUpHeight;
         }
         NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
-                NotificationContentView.VISIBLE_TYPE_HEADSUP);
+                VISIBLE_TYPE_HEADSUP);
         if (headsUpWrapper != null) {
             headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
         }
@@ -1511,7 +1580,14 @@
     }
 
     public void setNeedsRedaction(boolean needsRedaction) {
-        mNotificationInflater.setRedactAmbient(needsRedaction);
+        if (mNeedsRedaction != needsRedaction) {
+            mNeedsRedaction = needsRedaction;
+            updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, needsRedaction /* shouldInflate */);
+            mNotificationInflater.updateNeedsRedaction(needsRedaction);
+            if (!needsRedaction) {
+                freeContentViewWhenSafe(FLAG_CONTENT_VIEW_PUBLIC);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -2616,6 +2692,10 @@
         return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
     }
 
+    public View getExpandedContentView() {
+        return getPrivateLayout().getExpandedChild();
+    }
+
     public void setLegacy(boolean legacy) {
         for (NotificationContentView l : mLayouts) {
             l.setLegacy(legacy);
@@ -3017,6 +3097,36 @@
         boolean onClick(View v, int x, int y, MenuItem item);
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+        pw.println("  Notification: " + getStatusBarNotification().getKey());
+        pw.print("    visibility: " + getVisibility());
+        pw.print(", alpha: " + getAlpha());
+        pw.print(", translation: " + getTranslation());
+        pw.print(", removed: " + isRemoved());
+        pw.print(", privateShowing: " + (getShowingLayout() == mPrivateLayout));
+        pw.println();
+        pw.print("    ");
+        if (mNotificationViewState != null) {
+            mNotificationViewState.dump(fd, pw, args);
+        } else {
+            pw.print("no viewState!!!");
+        }
+        pw.println();
+        pw.println();
+        if (mIsSummaryWithChildren) {
+            List<ExpandableNotificationRow> notificationChildren = getNotificationChildren();
+            pw.println("  Children: " + notificationChildren.size());
+            pw.println("  {");
+            for(ExpandableNotificationRow child : notificationChildren) {
+                child.dump(fd, pw, args);
+            }
+            pw.println("  }");
+            pw.println();
+        }
+    }
+
     /**
      * Background task for executing IPCs to check if the notification is a system notification. The
      * output is used for both the blocking helper and the notification info.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 46019e3..38d657b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -25,16 +25,19 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
  * An abstract view for expandable views.
  */
-public abstract class ExpandableView extends FrameLayout {
+public abstract class ExpandableView extends FrameLayout implements Dumpable {
 
     public static final float NO_ROUNDNESS = -1;
     protected OnHeightChangedListener mOnHeightChangedListener;
@@ -559,6 +562,10 @@
         return false;
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    }
+
     /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 4ef8dbb..11cf8e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
@@ -23,6 +24,7 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -108,6 +110,10 @@
     private NotificationGroupManager mGroupManager;
     private RemoteInputController mRemoteInputController;
     private Runnable mExpandedVisibleListener;
+    /**
+     * List of listeners for when content views become inactive (i.e. not the showing view).
+     */
+    private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
 
     private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -418,11 +424,24 @@
         return mAmbientSingleLineChild;
     }
 
-    public void setContractedChild(View child) {
+    /**
+     * Sets the contracted view. Child may be null to remove the content view.
+     *
+     * @param child contracted content view to set
+     */
+    public void setContractedChild(@Nullable View child) {
         if (mContractedChild != null) {
             mContractedChild.animate().cancel();
             removeView(mContractedChild);
         }
+        if (child == null) {
+            mContractedChild = null;
+            mContractedWrapper = null;
+            if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
+                mTransformationStartVisibleType = UNDEFINED;
+            }
+            return;
+        }
         addView(child);
         mContractedChild = child;
         mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
@@ -445,7 +464,12 @@
         return null;
     }
 
-    public void setExpandedChild(View child) {
+    /**
+     * Sets the expanded view. Child may be null to remove the content view.
+     *
+     * @param child expanded content view to set
+     */
+    public void setExpandedChild(@Nullable View child) {
         if (mExpandedChild != null) {
             mPreviousExpandedRemoteInputIntent = null;
             if (mExpandedRemoteInput != null) {
@@ -478,7 +502,12 @@
                 mContainingNotification);
     }
 
-    public void setHeadsUpChild(View child) {
+    /**
+     * Sets the heads up view. Child may be null to remove the content view.
+     *
+     * @param child heads up content view to set
+     */
+    public void setHeadsUpChild(@Nullable View child) {
         if (mHeadsUpChild != null) {
             mPreviousHeadsUpRemoteInputIntent = null;
             if (mHeadsUpRemoteInput != null) {
@@ -511,12 +540,25 @@
                 mContainingNotification);
     }
 
-    public void setAmbientChild(View child) {
+    /**
+     * Sets the ambient view. Child may be null to remove the content view.
+     *
+     * @param child ambient content view to set
+     */
+    public void setAmbientChild(@Nullable View child) {
         if (mAmbientChild != null) {
             mAmbientChild.animate().cancel();
             removeView(mAmbientChild);
         }
         if (child == null) {
+            mAmbientChild = null;
+            mAmbientWrapper = null;
+            if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
+                mVisibleType = VISIBLE_TYPE_CONTRACTED;
+            }
+            if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
+                mTransformationStartVisibleType = UNDEFINED;
+            }
             return;
         }
         addView(child);
@@ -529,6 +571,13 @@
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
         updateVisibility();
+        if (visibility != VISIBLE) {
+            // View is no longer visible so all content views are inactive.
+            for (Runnable r : mOnContentViewInactiveListeners.values()) {
+                r.run();
+            }
+            mOnContentViewInactiveListeners.clear();
+        }
     }
 
     private void updateVisibility() {
@@ -576,6 +625,12 @@
         mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
         selectLayout(mAnimate /* animate */, false /* force */);
 
+        if (mContractedChild == null) {
+            // Contracted child may be null if this is the public content view and we don't need to
+            // show it.
+            return;
+        }
+
         int minHeightHint = getMinContentHeightHint();
 
         NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
@@ -726,7 +781,7 @@
     }
 
     public int getMaxHeight() {
-        if (mContainingNotification.isOnAmbient()) {
+        if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
             return getShowingAmbientView().getHeight();
         } else if (mExpandedChild != null) {
             return getViewHeight(VISIBLE_TYPE_EXPANDED)
@@ -734,8 +789,10 @@
         } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
             return getViewHeight(VISIBLE_TYPE_HEADSUP)
                     + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+        } else if (mContractedChild != null) {
+            return getViewHeight(VISIBLE_TYPE_CONTRACTED);
         }
-        return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+        return mNotificationMaxHeight;
     }
 
     private int getViewHeight(int visibleType) {
@@ -753,10 +810,11 @@
     }
 
     public int getMinHeight(boolean likeGroupExpanded) {
-        if (mContainingNotification.isOnAmbient()) {
+        if (mContainingNotification.isOnAmbient() && getShowingAmbientView() != null) {
             return getShowingAmbientView().getHeight();
         } else if (likeGroupExpanded || !mIsChildInGroup || isGroupExpanded()) {
-            return getViewHeight(VISIBLE_TYPE_CONTRACTED);
+            return mContractedChild != null
+                    ? getViewHeight(VISIBLE_TYPE_CONTRACTED) : mMinContractedHeight;
         } else {
             return mSingleLineView.getHeight();
         }
@@ -1091,7 +1149,8 @@
                 return VISIBLE_TYPE_EXPANDED;
             }
         } else {
-            if (noExpandedChild || (viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
+            if (noExpandedChild || (mContractedChild != null
+                    && viewHeight <= getViewHeight(VISIBLE_TYPE_CONTRACTED)
                     && (!mIsChildInGroup || isGroupExpanded()
                             || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
                 return VISIBLE_TYPE_CONTRACTED;
@@ -1163,6 +1222,7 @@
 
     public void onNotificationUpdated(NotificationData.Entry entry) {
         mStatusBarNotification = entry.notification;
+        mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateAllSingleLineViews();
         if (mContractedChild != null) {
@@ -1620,15 +1680,65 @@
         fireExpandedVisibleListenerIfVisible();
     }
 
+    /**
+     * Set a one-shot listener to run when a given content view becomes inactive.
+     *
+     * @param visibleType visible type corresponding to the content view to listen
+     * @param listener runnable to run once when the content view becomes inactive
+     */
+    public void performWhenContentInactive(int visibleType, Runnable listener) {
+        View view = getViewForVisibleType(visibleType);
+        // View is already inactive
+        if (view == null || isContentViewInactive(visibleType)) {
+            listener.run();
+            return;
+        }
+        mOnContentViewInactiveListeners.put(view, listener);
+    }
+
+    /**
+     * Whether or not the content view is inactive.  This means it should not be visible
+     * or the showing content as removing it would cause visual jank.
+     *
+     * @param visibleType visible type corresponding to the content view to be removed
+     * @return true if the content view is inactive, false otherwise
+     */
+    public boolean isContentViewInactive(int visibleType) {
+        View view = getViewForVisibleType(visibleType);
+        return isContentViewInactive(view);
+    }
+
+    /**
+     * Whether or not the content view is inactive.
+     *
+     * @param view view to see if its inactive
+     * @return true if the view is inactive, false o/w
+     */
+    private boolean isContentViewInactive(View view) {
+        if (view == null) {
+            return true;
+        }
+        return !isShown()
+                || (view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view);
+    }
+
+    @Override
+    protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
+        super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
+        if (isContentViewInactive(child)) {
+            Runnable listener = mOnContentViewInactiveListeners.remove(child);
+            if (listener != null) {
+                listener.run();
+            }
+        }
+    }
+
     public void setIsLowPriority(boolean isLowPriority) {
         mIsLowPriority = isLowPriority;
     }
 
     public boolean isDimmable() {
-        if (!mContractedWrapper.isDimmable()) {
-            return false;
-        }
-        return true;
+        return mContractedWrapper != null && mContractedWrapper.isDimmable();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index f4ef0f8..2499952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -41,15 +42,23 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -71,14 +80,20 @@
     // Dependencies:
     private final NotificationLockscreenUserManager mLockscreenUserManager =
             Dependency.get(NotificationLockscreenUserManager.class);
+    private final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
+    private final DeviceProvisionedController mDeviceProvisionedController =
+            Dependency.get(DeviceProvisionedController.class);
+    private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
 
     // which notification is currently being longpress-examined by the user
+    private final IStatusBarService mBarService;
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
-    private NotificationPresenter mPresenter;
     private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
+    private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
-    private NotificationInfo.CheckSaveListener mCheckSaveListener;
+    private CheckSaveListener mCheckSaveListener;
     private OnSettingsClickListener mOnSettingsClickListener;
     @VisibleForTesting
     protected String mKeyToRemoveOnGutsClosed;
@@ -89,16 +104,17 @@
 
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
-            NotificationInfo.CheckSaveListener checkSaveListener,
-            OnSettingsClickListener onSettingsClickListener) {
+            CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
         mPresenter = presenter;
         mListContainer = listContainer;
-        mCheckSaveListener = checkSaveListener;
-        mOnSettingsClickListener = onSettingsClickListener;
+        mCheckSaveListener = checkSave;
+        mOnSettingsClickListener = onSettingsClick;
     }
 
     public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
@@ -264,7 +280,7 @@
             onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
                 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
                 guts.resetFalsingCheck();
-                mOnSettingsClickListener.onClick(sbn.getKey());
+                mOnSettingsClickListener.onSettingsClick(sbn.getKey());
                 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
             };
         }
@@ -279,7 +295,7 @@
                 mCheckSaveListener,
                 onSettingsClick,
                 onAppSettingsClick,
-                mPresenter.isDeviceProvisioned(),
+                mDeviceProvisionedController.isDeviceProvisioned(),
                 row.getIsNonblockable(),
                 isForBlockingHelper,
                 row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
@@ -317,6 +333,10 @@
         mNotificationGutsExposed = guts;
     }
 
+    public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+        return this::openGuts;
+    }
+
     /**
      * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
      * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
@@ -385,7 +405,7 @@
                 guts.setVisibility(View.VISIBLE);
 
                 final boolean needsFalsingProtection =
-                        (mPresenter.isPresenterLocked() &&
+                        (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
                                 !mAccessibilityManager.isTouchExplorationEnabled());
 
                 guts.openControls(
@@ -442,6 +462,6 @@
     }
 
     public interface OnSettingsClickListener {
-        void onClick(String key);
+        public void onSettingsClick(String key);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index aa4765a..38d6b35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -16,12 +16,18 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.content.Context;
 import android.os.AsyncTask;
 import android.os.CancellationSignal;
 import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.View;
 import android.widget.RemoteViews;
@@ -30,11 +36,13 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.Assert;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -52,14 +60,67 @@
 public class NotificationInflater {
 
     public static final String TAG = "NotificationInflater";
-    @VisibleForTesting
-    static final int FLAG_REINFLATE_ALL = ~0;
-    private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
-    @VisibleForTesting
-    static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
-    private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
-    private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
-    private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true,
+            prefix = {"FLAG_CONTENT_VIEW_"},
+            value = {
+                FLAG_CONTENT_VIEW_CONTRACTED,
+                FLAG_CONTENT_VIEW_EXPANDED,
+                FLAG_CONTENT_VIEW_HEADS_UP,
+                FLAG_CONTENT_VIEW_AMBIENT,
+                FLAG_CONTENT_VIEW_PUBLIC,
+                FLAG_CONTENT_VIEW_ALL})
+    public @interface InflationFlag {}
+    /**
+     * The default, contracted view.  Seen when the shade is pulled down and in the lock screen
+     * if there is no worry about content sensitivity.
+     */
+    public static final int FLAG_CONTENT_VIEW_CONTRACTED = 1;
+
+    /**
+     * The expanded view.  Seen when the user expands a notification.
+     */
+    public static final int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1;
+
+    /**
+     * The heads up view.  Seen when a high priority notification peeks in from the top.
+     */
+    public static final int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2;
+
+    /**
+     * The ambient view.  Seen when a high priority notification is received and the phone
+     * is dozing.
+     */
+    public static final int FLAG_CONTENT_VIEW_AMBIENT = 1 << 3;
+
+    /**
+     * The public view.  This is a version of the contracted view that hides sensitive
+     * information and is used on the lock screen if we determine that the notification's
+     * content should be hidden.
+     */
+    public static final int FLAG_CONTENT_VIEW_PUBLIC = 1 << 4;
+
+    public static final int FLAG_CONTENT_VIEW_ALL = ~0;
+
+    // TODO: Heads up and ambient are always inflated as a temporary workaround.
+    // See http://b/117933032 and http://b/117894786
+    /**
+     * Content views that must be inflated at all times.
+     */
+    @InflationFlag
+    private static final int REQUIRED_INFLATION_FLAGS =
+            FLAG_CONTENT_VIEW_CONTRACTED
+            | FLAG_CONTENT_VIEW_EXPANDED
+            | FLAG_CONTENT_VIEW_HEADS_UP
+            | FLAG_CONTENT_VIEW_AMBIENT;
+
+    /**
+     * The set of content views to inflate.
+     */
+    @InflationFlag
+    private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
+
     private static final InflationExecutor EXECUTOR = new InflationExecutor();
 
     private final ExpandableNotificationRow mRow;
@@ -71,6 +132,7 @@
     private InflationCallback mCallback;
     private boolean mRedactAmbient;
     private List<Notification.Action> mSmartActions;
+    private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
 
     public NotificationInflater(ExpandableNotificationRow row) {
         mRow = row;
@@ -89,10 +151,10 @@
         if (childInGroup != mIsChildInGroup) {
             mIsChildInGroup = childInGroup;
             if (mIsLowPriority) {
-                int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
+                int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
                 inflateNotificationViews(flags);
             }
-        } ;
+        }
     }
 
     public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
@@ -111,14 +173,60 @@
         mRemoteViewClickHandler = remoteViewClickHandler;
     }
 
-    public void setRedactAmbient(boolean redactAmbient) {
-        if (mRedactAmbient != redactAmbient) {
-            mRedactAmbient = redactAmbient;
-            if (mRow.getEntry() == null) {
-                return;
-            }
-            inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+    /**
+     * Update whether or not the notification is redacted on the lock screen.  If the notification
+     * is now redacted, we should inflate the public contracted view and public ambient view to
+     * now show on the lock screen.
+     *
+     * @param needsRedaction true if the notification should now be redacted on the lock screen
+     */
+    public void updateNeedsRedaction(boolean needsRedaction) {
+        mRedactAmbient = needsRedaction;
+        if (mRow.getEntry() == null) {
+            return;
         }
+        int flags = FLAG_CONTENT_VIEW_AMBIENT;
+        if (needsRedaction) {
+            flags |= FLAG_CONTENT_VIEW_PUBLIC;
+        }
+        inflateNotificationViews(flags);
+    }
+
+    /**
+     * Set whether or not a particular content view is needed and whether or not it should be
+     * inflated.  These flags will be used when we inflate or reinflate.
+     *
+     * @param flag the {@link InflationFlag} corresponding to the view that should/should not be
+     *             inflated
+     * @param shouldInflate true if the view should be inflated, false otherwise
+     */
+    public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
+        if (shouldInflate) {
+            mInflationFlags |= flag;
+        } else if ((REQUIRED_INFLATION_FLAGS & flag) == 0) {
+            mInflationFlags &= ~flag;
+        }
+    }
+
+    /**
+     * Add flags for which content views should be inflated in addition to those already set.
+     *
+     * @param flags a set of {@link InflationFlag} corresponding to content views that should be
+     *              inflated
+     */
+    public void addInflationFlags(@InflationFlag int flags) {
+        mInflationFlags |= flags;
+    }
+
+    /**
+     * Whether or not the view corresponding to the flag is set to be inflated currently.
+     *
+     * @param flag the {@link InflationFlag} corresponding to the view
+     * @return true if the flag is set and view will be inflated, false o/w
+     */
+    @VisibleForTesting
+    public boolean isInflationFlagSet(@InflationFlag int flag) {
+        return ((mInflationFlags & flag) != 0);
     }
 
     /**
@@ -126,29 +234,32 @@
      * notify the callback once it's finished.
      */
     public void inflateNotificationViews() {
-        inflateNotificationViews(FLAG_REINFLATE_ALL);
+        inflateNotificationViews(mInflationFlags);
     }
 
     /**
-     * Reinflate all views for the specified flags on a background thread. This is asynchronous and
-     * will notify the callback once it's finished.
+     * Inflate all views for the specified flags on a background thread.  This is asynchronous and
+     * will notify the callback once it's finished.  If the content view is already inflated, this
+     * will reinflate it.
      *
-     * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
-     *                       to reinflate all of views.
+     * @param reInflateFlags flags which views should be inflated.  Should be a subset of
+     *                       {@link NotificationInflater#mInflationFlags} as only those will be
+     *                       inflated/reinflated.
      */
-    @VisibleForTesting
-    void inflateNotificationViews(int reInflateFlags) {
+    private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
         if (mRow.isRemoved()) {
             // We don't want to reinflate anything for removed notifications. Otherwise views might
             // be readded to the stack, leading to leaks. This may happen with low-priority groups
             // where the removal of already removed children can lead to a reinflation.
             return;
         }
+        // Only inflate the ones that are set.
+        reInflateFlags &= mInflationFlags;
         StatusBarNotification sbn = mRow.getEntry().notification;
-        AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
-                mIsLowPriority,
-                mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
-                mCallback, mRemoteViewClickHandler, mSmartActions);
+        AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
+                mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
+                mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler,
+                mSmartActions);
         if (mCallback != null && mCallback.doInflateSynchronous()) {
             task.onPostExecute(task.doInBackground());
         } else {
@@ -157,38 +268,85 @@
     }
 
     @VisibleForTesting
-    InflationProgress inflateNotificationViews(int reInflateFlags,
+    InflationProgress inflateNotificationViews(@InflationFlag int reInflateFlags,
             Notification.Builder builder, Context packageContext) {
         InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
                 mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
                 mRedactAmbient, packageContext);
-        apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null);
+        apply(result, reInflateFlags, mCachedContentViews, mRow, mRedactAmbient,
+                mRemoteViewClickHandler, null);
         return result;
     }
 
-    private static InflationProgress createRemoteViews(int reInflateFlags,
+    /**
+     * Frees the content view associated with the inflation flag.  Will only succeed if the
+     * view is safe to remove.
+     *
+     * @param inflateFlag the flag corresponding to the content view which should be freed
+     */
+    public void freeNotificationView(@InflationFlag int inflateFlag) {
+        if ((mInflationFlags & inflateFlag) != 0) {
+            // The view should still be inflated.
+            return;
+        }
+        switch (inflateFlag) {
+            case FLAG_CONTENT_VIEW_HEADS_UP:
+                if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
+                    mRow.getPrivateLayout().setHeadsUpChild(null);
+                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
+                }
+                break;
+            case FLAG_CONTENT_VIEW_AMBIENT:
+                boolean privateSafeToRemove = mRow.getPrivateLayout().isContentViewInactive(
+                        VISIBLE_TYPE_AMBIENT);
+                boolean publicSafeToRemove = mRow.getPublicLayout().isContentViewInactive(
+                        VISIBLE_TYPE_AMBIENT);
+                if (privateSafeToRemove) {
+                    mRow.getPrivateLayout().setAmbientChild(null);
+                }
+                if (publicSafeToRemove) {
+                    mRow.getPublicLayout().setAmbientChild(null);
+                }
+                if (privateSafeToRemove && publicSafeToRemove) {
+                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_AMBIENT);
+                }
+                break;
+            case FLAG_CONTENT_VIEW_PUBLIC:
+                if (mRow.getPublicLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
+                    mRow.getPublicLayout().setContractedChild(null);
+                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_PUBLIC);
+                }
+                break;
+            case FLAG_CONTENT_VIEW_CONTRACTED:
+            case FLAG_CONTENT_VIEW_EXPANDED:
+            default:
+                break;
+        }
+    }
+
+    private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
             Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
             boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
             Context packageContext) {
         InflationProgress result = new InflationProgress();
         isLowPriority = isLowPriority && !isChildInGroup;
-        if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
             result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
             result.newExpandedView = createExpandedView(builder, isLowPriority);
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
             result.newPublicView = builder.makePublicContentView();
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
             result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
                     : builder.makeAmbientNotification();
         }
@@ -199,18 +357,20 @@
         return result;
     }
 
-    public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
+    public static CancellationSignal apply(InflationProgress result,
+            @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
             ExpandableNotificationRow row, boolean redactAmbient,
             RemoteViews.OnClickHandler remoteViewClickHandler,
             @Nullable InflationCallback callback) {
-        NotificationData.Entry entry = row.getEntry();
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
 
-        int flag = FLAG_REINFLATE_CONTENT_VIEW;
+        int flag = FLAG_CONTENT_VIEW_CONTRACTED;
         if ((reInflateFlags & flag) != 0) {
-            boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView);
+            boolean isNewView =
+                    !canReapplyRemoteView(result.newContentView,
+                            cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -222,18 +382,19 @@
                     return result.newContentView;
                 }
             };
-            applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
-                    isNewView, remoteViewClickHandler, callback, entry, privateLayout,
+            applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, redactAmbient,
+                    isNewView, remoteViewClickHandler, callback, privateLayout,
                     privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
                             NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                     runningInflations, applyCallback);
         }
 
-        flag = FLAG_REINFLATE_EXPANDED_VIEW;
+        flag = FLAG_CONTENT_VIEW_EXPANDED;
         if ((reInflateFlags & flag) != 0) {
             if (result.newExpandedView != null) {
-                boolean isNewView = !canReapplyRemoteView(result.newExpandedView,
-                        entry.cachedBigContentView);
+                boolean isNewView =
+                        !canReapplyRemoteView(result.newExpandedView,
+                                cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
                 ApplyCallback applyCallback = new ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -245,8 +406,8 @@
                         return result.newExpandedView;
                     }
                 };
-                applyRemoteView(result, reInflateFlags, flag, row,
-                        redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+                applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+                        redactAmbient, isNewView, remoteViewClickHandler, callback,
                         privateLayout, privateLayout.getExpandedChild(),
                         privateLayout.getVisibleWrapper(
                                 NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -254,11 +415,12 @@
             }
         }
 
-        flag = FLAG_REINFLATE_HEADS_UP_VIEW;
+        flag = FLAG_CONTENT_VIEW_HEADS_UP;
         if ((reInflateFlags & flag) != 0) {
             if (result.newHeadsUpView != null) {
-                boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView,
-                        entry.cachedHeadsUpContentView);
+                boolean isNewView =
+                        !canReapplyRemoteView(result.newHeadsUpView,
+                                cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
                 ApplyCallback applyCallback = new ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -270,19 +432,20 @@
                         return result.newHeadsUpView;
                     }
                 };
-                applyRemoteView(result, reInflateFlags, flag, row,
-                        redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+                applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+                        redactAmbient, isNewView, remoteViewClickHandler, callback,
                         privateLayout, privateLayout.getHeadsUpChild(),
                         privateLayout.getVisibleWrapper(
-                                NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations,
+                                VISIBLE_TYPE_HEADSUP), runningInflations,
                         applyCallback);
             }
         }
 
-        flag = FLAG_REINFLATE_PUBLIC_VIEW;
+        flag = FLAG_CONTENT_VIEW_PUBLIC;
         if ((reInflateFlags & flag) != 0) {
-            boolean isNewView = !canReapplyRemoteView(result.newPublicView,
-                    entry.cachedPublicContentView);
+            boolean isNewView =
+                    !canReapplyRemoteView(result.newPublicView,
+                            cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -294,18 +457,19 @@
                     return result.newPublicView;
                 }
             };
-            applyRemoteView(result, reInflateFlags, flag, row,
-                    redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+            applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+                    redactAmbient, isNewView, remoteViewClickHandler, callback,
                     publicLayout, publicLayout.getContractedChild(),
                     publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
                     runningInflations, applyCallback);
         }
 
-        flag = FLAG_REINFLATE_AMBIENT_VIEW;
+        flag = FLAG_CONTENT_VIEW_AMBIENT;
         if ((reInflateFlags & flag) != 0) {
             NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
-            boolean isNewView = !canReapplyAmbient(row, redactAmbient) ||
-                    !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView);
+            boolean isNewView = (!canReapplyAmbient(row, redactAmbient)
+                    || !canReapplyRemoteView(result.newAmbientView,
+                            cachedContentViews.get(FLAG_CONTENT_VIEW_AMBIENT)));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -317,15 +481,15 @@
                     return result.newAmbientView;
                 }
             };
-            applyRemoteView(result, reInflateFlags, flag, row,
-                    redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+            applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+                    redactAmbient, isNewView, remoteViewClickHandler, callback,
                     newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper(
                             NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations,
                     applyCallback);
         }
 
         // Let's try to finish, maybe nobody is even inflating anything
-        finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+        finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row,
                 redactAmbient);
         CancellationSignal cancellationSignal = new CancellationSignal();
         cancellationSignal.setOnCancelListener(
@@ -335,11 +499,11 @@
 
     @VisibleForTesting
     static void applyRemoteView(final InflationProgress result,
-            final int reInflateFlags, int inflationId,
-            final ExpandableNotificationRow row,
-            final boolean redactAmbient, boolean isNewView,
+            final @InflationFlag int reInflateFlags, @InflationFlag int inflationId,
+            final ArrayMap<Integer, RemoteViews> cachedContentViews,
+            final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView,
             RemoteViews.OnClickHandler remoteViewClickHandler,
-            @Nullable final InflationCallback callback, NotificationData.Entry entry,
+            @Nullable final InflationCallback callback,
             NotificationContentView parentLayout, View existingView,
             NotificationViewWrapper existingWrapper,
             final HashMap<Integer, CancellationSignal> runningInflations,
@@ -362,7 +526,7 @@
                     existingWrapper.onReinflated();
                 }
             } catch (Exception e) {
-                handleInflationError(runningInflations, e, entry.notification, callback);
+                handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback);
                 // Add a running inflation to make sure we don't trigger callbacks.
                 // Safe to do because only happens in tests.
                 runningInflations.put(inflationId, new CancellationSignal());
@@ -381,8 +545,8 @@
                     existingWrapper.onReinflated();
                 }
                 runningInflations.remove(inflationId);
-                finishIfDone(result, reInflateFlags, runningInflations, callback, row,
-                        redactAmbient);
+                finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
+                        callback, row, redactAmbient);
             }
 
             @Override
@@ -407,7 +571,8 @@
                     onViewApplied(newView);
                 } catch (Exception anotherException) {
                     runningInflations.remove(inflationId);
-                    handleInflationError(runningInflations, e, entry.notification, callback);
+                    handleInflationError(runningInflations, e, row.getStatusBarNotification(),
+                            callback);
                 }
             }
         };
@@ -430,8 +595,9 @@
         runningInflations.put(inflationId, cancellationSignal);
     }
 
-    private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations,
-            Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) {
+    private static void handleInflationError(
+            HashMap<Integer, CancellationSignal> runningInflations, Exception e,
+            StatusBarNotification notification, @Nullable InflationCallback callback) {
         Assert.isMainThread();
         runningInflations.values().forEach(CancellationSignal::cancel);
         if (callback != null) {
@@ -444,7 +610,8 @@
      *
      * @return true if the inflation was finished
      */
-    private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
+    private static boolean finishIfDone(InflationProgress result,
+            @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
             HashMap<Integer, CancellationSignal> runningInflations,
             @Nullable InflationCallback endListener, ExpandableNotificationRow row,
             boolean redactAmbient) {
@@ -453,40 +620,40 @@
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         if (runningInflations.isEmpty()) {
-            if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
                 if (result.inflatedContentView != null) {
                     privateLayout.setContractedChild(result.inflatedContentView);
                 }
-                entry.cachedContentView = result.newContentView;
+                cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
                 if (result.inflatedExpandedView != null) {
                     privateLayout.setExpandedChild(result.inflatedExpandedView);
                 } else if (result.newExpandedView == null) {
                     privateLayout.setExpandedChild(null);
                 }
-                entry.cachedBigContentView = result.newExpandedView;
+                cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
                 row.setExpandable(result.newExpandedView != null);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
                 if (result.inflatedHeadsUpView != null) {
                     privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
                 } else if (result.newHeadsUpView == null) {
                     privateLayout.setHeadsUpChild(null);
                 }
-                entry.cachedHeadsUpContentView = result.newHeadsUpView;
+                cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
                 if (result.inflatedPublicView != null) {
                     publicLayout.setContractedChild(result.inflatedPublicView);
                 }
-                entry.cachedPublicContentView = result.newPublicView;
+                cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
                 if (result.inflatedAmbientView != null) {
                     NotificationContentView newParent = redactAmbient
                             ? publicLayout : privateLayout;
@@ -495,12 +662,12 @@
                     newParent.setAmbientChild(result.inflatedAmbientView);
                     otherParent.setAmbientChild(null);
                 }
-                entry.cachedAmbientContentView = result.newAmbientView;
+                cachedContentViews.put(FLAG_CONTENT_VIEW_AMBIENT, result.newAmbientView);
             }
             entry.headsUpStatusBarText = result.headsUpStatusBarText;
             entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
             if (endListener != null) {
-                endListener.onAsyncInflationFinished(row.getEntry());
+                endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
             }
             return true;
         }
@@ -552,7 +719,15 @@
 
     public interface InflationCallback {
         void handleInflationException(StatusBarNotification notification, Exception e);
-        void onAsyncInflationFinished(NotificationData.Entry entry);
+
+        /**
+         * Callback for after the content views finish inflating.
+         *
+         * @param entry the entry with the content views set
+         * @param inflatedFlags the flags associated with the content views that were inflated
+         */
+        void onAsyncInflationFinished(NotificationData.Entry entry,
+                @InflationFlag int inflatedFlags);
 
         /**
          * Used to disable async-ness for tests. Should only be used for tests.
@@ -563,18 +738,13 @@
     }
 
     public void clearCachesAndReInflate() {
-        NotificationData.Entry entry = mRow.getEntry();
-        entry.cachedAmbientContentView = null;
-        entry.cachedBigContentView = null;
-        entry.cachedContentView = null;
-        entry.cachedHeadsUpContentView = null;
-        entry.cachedPublicContentView = null;
+        mCachedContentViews.clear();
         inflateNotificationViews();
     }
 
     private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
         NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
-                : row.getPrivateLayout();            ;
+                : row.getPrivateLayout();
         return ambientView.getAmbientChild() != null;
     }
 
@@ -589,7 +759,8 @@
         private final InflationCallback mCallback;
         private final boolean mUsesIncreasedHeadsUpHeight;
         private final boolean mRedactAmbient;
-        private int mReInflateFlags;
+        private @InflationFlag int mReInflateFlags;
+        private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
         private ExpandableNotificationRow mRow;
         private Exception mError;
         private RemoteViews.OnClickHandler mRemoteViewClickHandler;
@@ -597,15 +768,16 @@
         private List<Notification.Action> mSmartActions;
 
         private AsyncInflationTask(StatusBarNotification notification,
-                int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
-                boolean isChildInGroup, boolean usesIncreasedHeight,
+                @InflationFlag int reInflateFlags,
+                ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row,
+                boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight,
                 boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
-                InflationCallback callback,
-                RemoteViews.OnClickHandler remoteViewClickHandler,
+                InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler,
                 List<Notification.Action> smartActions) {
             mRow = row;
             mSbn = notification;
             mReInflateFlags = reInflateFlags;
+            mCachedContentViews = cachedContentViews;
             mContext = mRow.getContext();
             mIsLowPriority = isLowPriority;
             mIsChildInGroup = isChildInGroup;
@@ -622,6 +794,7 @@
         }
 
         @VisibleForTesting
+        @InflationFlag
         public int getReInflateFlags() {
             return mReInflateFlags;
         }
@@ -642,10 +815,9 @@
                             packageContext);
                     processor.processNotification(notification, recoveredBuilder);
                 }
-                return createRemoteViews(mReInflateFlags,
-                        recoveredBuilder, mIsLowPriority, mIsChildInGroup,
-                        mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
-                        packageContext);
+                return createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority,
+                        mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
+                        mRedactAmbient, packageContext);
             } catch (Exception e) {
                 mError = e;
                 return null;
@@ -655,8 +827,8 @@
         @Override
         protected void onPostExecute(InflationProgress result) {
             if (mError == null) {
-                mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
-                        mRemoteViewClickHandler, this);
+                mCancellationSignal = apply(result, mReInflateFlags, mCachedContentViews, mRow,
+                        mRedactAmbient, mRemoteViewClickHandler, this);
             } else {
                 handleError(mError);
             }
@@ -706,10 +878,11 @@
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry) {
+        public void onAsyncInflationFinished(NotificationData.Entry entry,
+                @InflationFlag int inflatedFlags) {
             mRow.getEntry().onInflationTaskFinished();
             mRow.onNotificationUpdated();
-            mCallback.onAsyncInflationFinished(mRow.getEntry());
+            mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index fa75c71..cfb6d99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -22,6 +22,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -31,7 +32,8 @@
  * Interface representing the entity that contains notifications. It can have
  * notification views added and removed from it, and will manage displaying them to the user.
  */
-public interface NotificationListContainer {
+public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
+        VisibilityLocationProvider {
 
     /**
      * Called when a child is being transferred.
@@ -128,14 +130,6 @@
     ViewGroup getViewParentForNotification(NotificationData.Entry entry);
 
     /**
-     * Called when the height of an expandable view changes.
-     *
-     * @param view view whose height changed
-     * @param animate whether this change should be animated
-     */
-    void onHeightChanged(ExpandableView view, boolean animate);
-
-    /**
      * Resets the currently exposed menu view.
      *
      * @param animate whether to animate the closing/change of menu view
@@ -158,13 +152,6 @@
      */
     void cleanUpViewState(View view);
 
-    /**
-     * Returns whether an ExpandableNotificationRow is in a visible location or not.
-     *
-     * @param row
-     * @return true if row is in a visible location
-     */
-    boolean isInVisibleLocation(ExpandableNotificationRow row);
 
     /**
      * Sets a listener to listen for changes in notification locations.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 33ac390..30d1701 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -25,6 +25,7 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeAnimator;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -42,11 +43,6 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.ColorUtils;
-
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -69,6 +65,8 @@
 import android.widget.OverScroller;
 import android.widget.ScrollView;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
@@ -84,46 +82,46 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
-import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationSnooze;
-import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone.AnimationStateHandler;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -142,10 +140,8 @@
 /**
  * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack.
  */
-public class NotificationStackScrollLayout extends ViewGroup
-        implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener,
-        OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer,
-        ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable {
+public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter,
+        NotificationListContainer, ConfigurationListener, Dumpable {
 
     public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
     private static final String TAG = "StackScroller";
@@ -160,7 +156,6 @@
 
     private ExpandHelper mExpandHelper;
     private final NotificationSwipeHelper mSwipeHelper;
-    private boolean mSwipingInProgress;
     private int mCurrentStackHeight = Integer.MAX_VALUE;
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
@@ -197,7 +192,7 @@
     // Current padding, will be either mRegularTopPadding or mDarkTopPadding
     private int mTopPadding;
     // Distance between AOD separator and shelf
-    private int mDarkSeparatorPadding;
+    private int mDarkShelfPadding;
     private int mBottomMargin;
     private int mBottomInset = 0;
     private float mQsExpansionFraction;
@@ -344,7 +339,7 @@
     private float mDimAmount;
     private ValueAnimator mDimAnimator;
     private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>();
-    private Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
+    private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
             mDimAnimator = null;
@@ -426,8 +421,6 @@
     private Runnable mAnimateScroll = this::animateScroll;
     private int mCornerRadius;
     private int mSidePaddings;
-    private final int mSeparatorWidth;
-    private final int mSeparatorThickness;
     private final Rect mBackgroundAnimationRect = new Rect();
     private int mAntiBurnInOffsetX;
     private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
@@ -437,6 +430,8 @@
     private float mVerticalPanelTranslation;
     private final NotificationLockscreenUserManager mLockscreenUserManager =
             Dependency.get(NotificationLockscreenUserManager.class);
+    protected final NotificationGutsManager mGutsManager =
+            Dependency.get(NotificationGutsManager.class);
     private final Rect mTmpRect = new Rect();
     private final NotificationEntryManager mEntryManager =
             Dependency.get(NotificationEntryManager.class);
@@ -456,6 +451,7 @@
 
     private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
     private NotificationPanelView mNotificationPanel;
+    private final ShadeController mShadeController = Dependency.get(ShadeController.class);
 
     private final NotificationGutsManager
             mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
@@ -485,12 +481,12 @@
         mBgColor = context.getColor(R.color.notification_shade_background_color);
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
-        mExpandHelper = new ExpandHelper(getContext(), this,
+        mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
                 minHeight, maxHeight);
         mExpandHelper.setEventSource(this);
         mExpandHelper.setScrollAdapter(this);
-        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(),
-                getContext(), new NotificationMenuListener());
+        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
+                getContext(), mMenuEventListener);
         mStackScrollAlgorithm = createStackScrollAlgorithm(context);
         initView(context);
         mFalsingManager = FalsingManager.getInstance(context);
@@ -498,9 +494,7 @@
                 res.getBoolean(R.bool.config_drawNotificationBackground);
         mFadeNotificationsOnDismiss =
                 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
-        mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
-        mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
-        mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
+        mDarkShelfPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
         mRoundnessManager.setAnimatedChildren(mChildrenToAddAnimated);
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
         addOnExpandedHeightListener(mRoundnessManager::setExpanded);
@@ -530,7 +524,7 @@
 
         inflateEmptyShadeView();
         inflateFooterView();
-        mVisualStabilityManager.setVisibilityLocationProvider(this);
+        mVisualStabilityManager.setVisibilityLocationProvider(this::isInVisibleLocation);
         setLongPressListener(mEntryManager.getNotificationLongClicker());
     }
 
@@ -589,7 +583,7 @@
         return false;
     }
 
-  @ShadeViewRefactor(RefactorComponent.INPUT)
+  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
   public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
             public void setRemoteInputActive(NotificationData.Entry entry,
@@ -628,7 +622,7 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationSwipeActionHelper getSwipeActionHelper() {
         return mSwipeHelper;
     }
@@ -672,23 +666,15 @@
         final int lockScreenRight = getWidth() - mSidePaddings;
         final int lockScreenTop = mCurrentBounds.top;
         final int lockScreenBottom = mCurrentBounds.bottom;
-        int separatorWidth = 0;
-        int separatorThickness = 0;
-        if (mIconAreaController.hasShelfIconsWhenFullyDark()) {
-            separatorThickness = mSeparatorThickness;
-            separatorWidth = mSeparatorWidth;
-        }
-        final int darkLeft = getWidth() / 2 - separatorWidth / 2;
-        final int darkRight = darkLeft + separatorWidth;
-        final int darkTop = (int) (mRegularTopPadding + separatorThickness / 2f);
-        final int darkBottom = darkTop + separatorThickness;
+        final int darkLeft = getWidth() / 2;
+        final int darkTop = mRegularTopPadding;
 
         if (mAmbientState.hasPulsingNotifications()) {
             // No divider, we have a notification icon instead
         } else if (mAmbientState.isFullyDark()) {
             // Only draw divider on AOD if we actually have notifications
             if (mFirstVisibleBackgroundChild != null) {
-                canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
+                canvas.drawRect(darkLeft, darkTop, darkLeft, darkTop, mBackgroundPaint);
             }
         } else {
             float yProgress = 1 - mInterpolatedDarkAmount;
@@ -698,8 +684,8 @@
             mBackgroundAnimationRect.set(
                     (int) MathUtils.lerp(darkLeft, lockScreenLeft, xProgress),
                     (int) MathUtils.lerp(darkTop, lockScreenTop, yProgress),
-                    (int) MathUtils.lerp(darkRight, lockScreenRight, xProgress),
-                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, yProgress));
+                    (int) MathUtils.lerp(darkLeft, lockScreenRight, xProgress),
+                    (int) MathUtils.lerp(darkTop, lockScreenBottom, yProgress));
 
             if (!mAmbientState.isDark() || mFirstVisibleBackgroundChild != null) {
                 canvas.drawRoundRect(mBackgroundAnimationRect.left, mBackgroundAnimationRect.top,
@@ -1012,7 +998,7 @@
     private void setTopPadding(int topPadding, boolean animate) {
         if (mRegularTopPadding != topPadding) {
             mRegularTopPadding = topPadding;
-            mDarkTopPadding = topPadding + mDarkSeparatorPadding;
+            mDarkTopPadding = topPadding + mDarkShelfPadding;
             mAmbientState.setDarkTopPadding(mDarkTopPadding);
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
@@ -1245,11 +1231,6 @@
         return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
-        mLongPressListener = listener;
-    }
-
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
     public void setQsContainer(ViewGroup qsContainer) {
         mQsContainer = qsContainer;
@@ -1273,7 +1254,7 @@
         return false;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
         getLocationOnScreen(mTempInt2);
         float localTouchY = touchY - mTempInt2[1];
@@ -1303,16 +1284,8 @@
         return closestChild;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
-        getLocationOnScreen(mTempInt2);
-        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public ExpandableView getChildAtPosition(float touchX, float touchY) {
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+    private ExpandableView getChildAtPosition(float touchX, float touchY) {
         return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
 
     }
@@ -1325,7 +1298,7 @@
      * @param requireMinHeight Whether a minimum height is required for a child to be returned.
      * @return the child at the given location.
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private ExpandableView getChildAtPosition(float touchX, float touchY,
             boolean requireMinHeight) {
         // find the view under the pointer, accounting for GONE views
@@ -1365,71 +1338,9 @@
         return null;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    public boolean canChildBeExpanded(View v) {
-        return v instanceof ExpandableNotificationRow
-                && ((ExpandableNotificationRow) v).isExpandable()
-                && !((ExpandableNotificationRow) v).areGutsExposed()
-                && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
-    }
-
-    /* Only ever called as a consequence of an expansion gesture in the shade. */
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setUserExpandedChild(View v, boolean userExpanded) {
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            if (userExpanded && onKeyguard()) {
-                // Due to a race when locking the screen while touching, a notification may be
-                // expanded even after we went back to keyguard. An example of this happens if
-                // you click in the empty space while expanding a group.
-
-                // We also need to un-user lock it here, since otherwise the content height
-                // calculated might be wrong. We also can't invert the two calls since
-                // un-userlocking it will trigger a layout switch in the content view.
-                row.setUserLocked(false);
-                updateContentHeight();
-                notifyHeightChangeListener(row);
-                return;
-            }
-            row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
-            row.onExpandedByGesture(userExpanded);
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setExpansionCancelled(View v) {
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setUserLockedChild(View v, boolean userLocked) {
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).setUserLocked(userLocked);
-        }
-        cancelLongPress();
-        requestDisallowInterceptTouchEvent(true);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void expansionStateChanged(boolean isExpanding) {
-        mExpandingNotification = isExpanding;
-        if (!mExpandedInThisMotion) {
-            mMaxScrollAfterExpand = mOwnScrollY;
-            mExpandedInThisMotion = true;
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    public int getMaxExpandHeight(ExpandableView view) {
-        return view.getMaxContentHeight();
+    private ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+        getLocationOnScreen(mTempInt2);
+        return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -1526,14 +1437,6 @@
         return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void setSwipingInProgress(boolean isSwiped) {
-        mSwipingInProgress = isSwiped;
-        if (isSwiped) {
-            requestDisallowInterceptTouchEvent(true);
-        }
-    }
-
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onConfigurationChanged(Configuration newConfig) {
@@ -1567,249 +1470,6 @@
         return this;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onTouchEvent(MotionEvent ev) {
-        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
-                || ev.getActionMasked() == MotionEvent.ACTION_UP;
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
-            if (isCancelOrUp) {
-                mExpandHelper.onlyObserveMovements(false);
-            }
-            boolean wasExpandingBefore = mExpandingNotification;
-            expandWantsIt = mExpandHelper.onTouchEvent(ev);
-            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
-                    && !mDisallowScrollingInThisMotion) {
-                dispatchDownEventToScroller(ev);
-            }
-        }
-        boolean scrollerWantsIt = false;
-        if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
-                && !mDisallowScrollingInThisMotion) {
-            scrollerWantsIt = onScrollTouch(ev);
-        }
-        boolean horizontalSwipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
-        }
-
-        // Check if we need to clear any snooze leavebehinds
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
-                && guts.getGutsContent() instanceof NotificationSnooze) {
-            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
-            if ((ns.isExpanded() && isCancelOrUp)
-                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
-                // If the leavebehind is expanded we clear it on the next up event, otherwise we
-                // clear it on the next non-horizontal swipe or expand event.
-                checkSnoozeLeavebehind();
-            }
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void dispatchDownEventToScroller(MotionEvent ev) {
-        MotionEvent downEvent = MotionEvent.obtain(ev);
-        downEvent.setAction(MotionEvent.ACTION_DOWN);
-        onScrollTouch(downEvent);
-        downEvent.recycle();
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
-                || mDisallowScrollingInThisMotion) {
-            return false;
-        }
-        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_SCROLL: {
-                    if (!mIsBeingDragged) {
-                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
-                        if (vscroll != 0) {
-                            final int delta = (int) (vscroll * getVerticalScrollFactor());
-                            final int range = getScrollRange();
-                            int oldScrollY = mOwnScrollY;
-                            int newScrollY = oldScrollY - delta;
-                            if (newScrollY < 0) {
-                                newScrollY = 0;
-                            } else if (newScrollY > range) {
-                                newScrollY = range;
-                            }
-                            if (newScrollY != oldScrollY) {
-                                setOwnScrollY(newScrollY);
-                                return true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return super.onGenericMotionEvent(event);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private boolean onScrollTouch(MotionEvent ev) {
-        if (!isScrollingEnabled()) {
-            return false;
-        }
-        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
-            return false;
-        }
-        mForcedScroll = null;
-        initVelocityTrackerIfNotExists();
-        mVelocityTracker.addMovement(ev);
-
-        final int action = ev.getAction();
-
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                if (getChildCount() == 0 || !isInContentBounds(ev)) {
-                    return false;
-                }
-                boolean isBeingDragged = !mScroller.isFinished();
-                setIsBeingDragged(isBeingDragged);
-                /*
-                 * If being flinged and user touches, stop the fling. isFinished
-                 * will be false if being flinged.
-                 */
-                if (!mScroller.isFinished()) {
-                    mScroller.forceFinished(true);
-                }
-
-                // Remember where the motion event started
-                mLastMotionY = (int) ev.getY();
-                mDownX = (int) ev.getX();
-                mActivePointerId = ev.getPointerId(0);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE:
-                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (activePointerIndex == -1) {
-                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
-                    break;
-                }
-
-                final int y = (int) ev.getY(activePointerIndex);
-                final int x = (int) ev.getX(activePointerIndex);
-                int deltaY = mLastMotionY - y;
-                final int xDiff = Math.abs(x - mDownX);
-                final int yDiff = Math.abs(deltaY);
-                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
-                    setIsBeingDragged(true);
-                    if (deltaY > 0) {
-                        deltaY -= mTouchSlop;
-                    } else {
-                        deltaY += mTouchSlop;
-                    }
-                }
-                if (mIsBeingDragged) {
-                    // Scroll to follow the motion event
-                    mLastMotionY = y;
-                    int range = getScrollRange();
-                    if (mExpandedInThisMotion) {
-                        range = Math.min(range, mMaxScrollAfterExpand);
-                    }
-
-                    float scrollAmount;
-                    if (deltaY < 0) {
-                        scrollAmount = overScrollDown(deltaY);
-                    } else {
-                        scrollAmount = overScrollUp(deltaY, range);
-                    }
-
-                    // Calling customOverScrollBy will call onCustomOverScrolled, which
-                    // sets the scrolling if applicable.
-                    if (scrollAmount != 0.0f) {
-                        // The scrolling motion could not be compensated with the
-                        // existing overScroll, we have to scroll the view
-                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
-                                range, getHeight() / 2);
-                        // If we're scrolling, leavebehinds should be dismissed
-                        checkSnoozeLeavebehind();
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mIsBeingDragged) {
-                    final VelocityTracker velocityTracker = mVelocityTracker;
-                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
-                    if (shouldOverScrollFling(initialVelocity)) {
-                        onOverScrollFling(true, initialVelocity);
-                    } else {
-                        if (getChildCount() > 0) {
-                            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                                float currentOverScrollTop = getCurrentOverScrollAmount(true);
-                                if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
-                                    fling(-initialVelocity);
-                                } else {
-                                    onOverScrollFling(false, initialVelocity);
-                                }
-                            } else {
-                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
-                                        getScrollRange())) {
-                                    animateScroll();
-                                }
-                            }
-                        }
-                    }
-                    mActivePointerId = INVALID_POINTER;
-                    endDrag();
-                }
-
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
-                        animateScroll();
-                    }
-                    mActivePointerId = INVALID_POINTER;
-                    endDrag();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                mLastMotionY = (int) ev.getY(index);
-                mDownX = (int) ev.getX(index);
-                mActivePointerId = ev.getPointerId(index);
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP:
-                onSecondaryPointerUp(ev);
-                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
-                mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
-                break;
-        }
-        return true;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    protected boolean isInsideQsContainer(MotionEvent ev) {
-        return ev.getY() < mQsContainer.getBottom();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void onOverScrollFling(boolean open, int initialVelocity) {
-        if (mOverscrollTopChangedListener != null) {
-            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
-        }
-        mDontReportNextOverScroll = true;
-        setOverScrollAmount(0.0f, true, false);
-    }
-
     /**
      * Perform a scroll upwards and adapt the overscroll amounts accordingly
      *
@@ -1817,7 +1477,7 @@
      * @return The amount of scrolling to be performed by the scroller,
      * not handled by the overScroll amount.
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private float overScrollUp(int deltaY, int range) {
         deltaY = Math.max(deltaY, 0);
         float currentTopAmount = getCurrentOverScrollAmount(true);
@@ -1876,24 +1536,6 @@
         return scrollAmount;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void onSecondaryPointerUp(MotionEvent ev) {
-        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
-                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-        final int pointerId = ev.getPointerId(pointerIndex);
-        if (pointerId == mActivePointerId) {
-            // This was our active pointer going up. Choose a new
-            // active pointer and adjust accordingly.
-            // TODO: Make this decision more intelligent.
-            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
-            mLastMotionY = (int) ev.getY(newPointerIndex);
-            mActivePointerId = ev.getPointerId(newPointerIndex);
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void initVelocityTrackerIfNotExists() {
         if (mVelocityTracker == null) {
@@ -2636,7 +2278,7 @@
      *                  numbers mean that the finger/cursor is moving down the screen,
      *                  which means we want to scroll towards the top.
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void fling(int velocityY) {
         if (getChildCount() > 0) {
             int scrollRange = getScrollRange();
@@ -2674,7 +2316,7 @@
      * @return Whether a fling performed on the top overscroll edge lead to the expanded
      * overScroll view (i.e QS).
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean shouldOverScrollFling(int initialVelocity) {
         float topOverScroll = getCurrentOverScrollAmount(true);
         return mScrolledToTopOnFirstDown
@@ -2757,7 +2399,7 @@
         return Math.max(desiredPadding, mIntrinsicPadding);
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private float getRubberBandFactor(boolean onTop) {
         if (!onTop) {
             return RUBBER_BAND_FACTOR_NORMAL;
@@ -2777,99 +2419,13 @@
      * rubberbanded, false if it is technically an overscroll but rather a motion to expand the
      * overscroll view (e.g. expand QS).
      */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean isRubberbanded(boolean onTop) {
         return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
                 || !mScrolledToTopOnFirstDown;
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void endDrag() {
-        setIsBeingDragged(false);
 
-        recycleVelocityTracker();
-
-        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
-            setOverScrollAmount(0, true /* onTop */, true /* animate */);
-        }
-        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
-            setOverScrollAmount(0, false /* onTop */, true /* animate */);
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
-        ev.offsetLocation(sourceView.getX(), sourceView.getY());
-        ev.offsetLocation(-targetView.getX(), -targetView.getY());
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        initDownStates(ev);
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
-            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
-        }
-        boolean scrollWantsIt = false;
-        if (!mSwipingInProgress && !mExpandingNotification) {
-            scrollWantsIt = onInterceptTouchEventScroll(ev);
-        }
-        boolean swipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
-        }
-        // Check if we need to clear any snooze leavebehinds
-        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
-                !expandWantsIt && !scrollWantsIt) {
-            mCheckForLeavebehind = false;
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void handleEmptySpaceClick(MotionEvent ev) {
-        switch (ev.getActionMasked()) {
-            case MotionEvent.ACTION_MOVE:
-                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
-                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
-                    mTouchIsClick = false;
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
-                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
-                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
-                }
-                break;
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void initDownStates(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mExpandedInThisMotion = false;
-            mOnlyScrollingInThisMotion = !mScroller.isFinished();
-            mDisallowScrollingInThisMotion = false;
-            mDisallowDismissInThisMotion = false;
-            mTouchIsClick = true;
-            mInitialTouchX = ev.getX();
-            mInitialTouchY = ev.getY();
-        }
-    }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -2896,15 +2452,6 @@
         mCurrentStackScrollState.removeViewStateForView(child);
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
-        super.requestDisallowInterceptTouchEvent(disallowIntercept);
-        if (disallowIntercept) {
-            cancelLongPress();
-        }
-    }
-
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void onViewRemovedInternal(View child, ViewGroup container) {
         if (mChangePositionInProgress) {
@@ -3600,6 +3147,385 @@
         mGoToFullShadeNeedsAnimation = false;
     }
 
+    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
+    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
+        return new StackScrollAlgorithm(context);
+    }
+
+    /**
+     * @return Whether a y coordinate is inside the content.
+     */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    public boolean isInContentBounds(float y) {
+        return y < getHeight() - getEmptyBottomMargin();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
+        mLongPressListener = listener;
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean onTouchEvent(MotionEvent ev) {
+        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+                || ev.getActionMasked() == MotionEvent.ACTION_UP;
+        handleEmptySpaceClick(ev);
+        boolean expandWantsIt = false;
+        boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+        if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion) {
+            if (isCancelOrUp) {
+                mExpandHelper.onlyObserveMovements(false);
+            }
+            boolean wasExpandingBefore = mExpandingNotification;
+            expandWantsIt = mExpandHelper.onTouchEvent(ev);
+            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
+                    && !mDisallowScrollingInThisMotion) {
+                dispatchDownEventToScroller(ev);
+            }
+        }
+        boolean scrollerWantsIt = false;
+        if (mIsExpanded && !swipingInProgress && !mExpandingNotification
+                && !mDisallowScrollingInThisMotion) {
+            scrollerWantsIt = onScrollTouch(ev);
+        }
+        boolean horizontalSwipeWantsIt = false;
+        if (!mIsBeingDragged
+                && !mExpandingNotification
+                && !mExpandedInThisMotion
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
+            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+        }
+
+        // Check if we need to clear any snooze leavebehinds
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
+                && guts.getGutsContent() instanceof NotificationSnooze) {
+            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+            if ((ns.isExpanded() && isCancelOrUp)
+                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+                // If the leavebehind is expanded we clear it on the next up event, otherwise we
+                // clear it on the next non-horizontal swipe or expand event.
+                checkSnoozeLeavebehind();
+            }
+        }
+        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+            mCheckForLeavebehind = true;
+        }
+        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void dispatchDownEventToScroller(MotionEvent ev) {
+        MotionEvent downEvent = MotionEvent.obtain(ev);
+        downEvent.setAction(MotionEvent.ACTION_DOWN);
+        onScrollTouch(downEvent);
+        downEvent.recycle();
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        if (!isScrollingEnabled() || !mIsExpanded || mSwipeHelper.isSwipingInProgress() || mExpandingNotification
+                || mDisallowScrollingInThisMotion) {
+            return false;
+        }
+        if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_SCROLL: {
+                    if (!mIsBeingDragged) {
+                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
+                        if (vscroll != 0) {
+                            final int delta = (int) (vscroll * getVerticalScrollFactor());
+                            final int range = getScrollRange();
+                            int oldScrollY = mOwnScrollY;
+                            int newScrollY = oldScrollY - delta;
+                            if (newScrollY < 0) {
+                                newScrollY = 0;
+                            } else if (newScrollY > range) {
+                                newScrollY = range;
+                            }
+                            if (newScrollY != oldScrollY) {
+                                setOwnScrollY(newScrollY);
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return super.onGenericMotionEvent(event);
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private boolean onScrollTouch(MotionEvent ev) {
+        if (!isScrollingEnabled()) {
+            return false;
+        }
+        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
+            return false;
+        }
+        mForcedScroll = null;
+        initVelocityTrackerIfNotExists();
+        mVelocityTracker.addMovement(ev);
+
+        final int action = ev.getAction();
+
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                if (getChildCount() == 0 || !isInContentBounds(ev)) {
+                    return false;
+                }
+                boolean isBeingDragged = !mScroller.isFinished();
+                setIsBeingDragged(isBeingDragged);
+                /*
+                 * If being flinged and user touches, stop the fling. isFinished
+                 * will be false if being flinged.
+                 */
+                if (!mScroller.isFinished()) {
+                    mScroller.forceFinished(true);
+                }
+
+                // Remember where the motion event started
+                mLastMotionY = (int) ev.getY();
+                mDownX = (int) ev.getX();
+                mActivePointerId = ev.getPointerId(0);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE:
+                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+                    break;
+                }
+
+                final int y = (int) ev.getY(activePointerIndex);
+                final int x = (int) ev.getX(activePointerIndex);
+                int deltaY = mLastMotionY - y;
+                final int xDiff = Math.abs(x - mDownX);
+                final int yDiff = Math.abs(deltaY);
+                if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
+                    setIsBeingDragged(true);
+                    if (deltaY > 0) {
+                        deltaY -= mTouchSlop;
+                    } else {
+                        deltaY += mTouchSlop;
+                    }
+                }
+                if (mIsBeingDragged) {
+                    // Scroll to follow the motion event
+                    mLastMotionY = y;
+                    int range = getScrollRange();
+                    if (mExpandedInThisMotion) {
+                        range = Math.min(range, mMaxScrollAfterExpand);
+                    }
+
+                    float scrollAmount;
+                    if (deltaY < 0) {
+                        scrollAmount = overScrollDown(deltaY);
+                    } else {
+                        scrollAmount = overScrollUp(deltaY, range);
+                    }
+
+                    // Calling customOverScrollBy will call onCustomOverScrolled, which
+                    // sets the scrolling if applicable.
+                    if (scrollAmount != 0.0f) {
+                        // The scrolling motion could not be compensated with the
+                        // existing overScroll, we have to scroll the view
+                        customOverScrollBy((int) scrollAmount, mOwnScrollY,
+                                range, getHeight() / 2);
+                        // If we're scrolling, leavebehinds should be dismissed
+                        checkSnoozeLeavebehind();
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mIsBeingDragged) {
+                    final VelocityTracker velocityTracker = mVelocityTracker;
+                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+                    if (shouldOverScrollFling(initialVelocity)) {
+                        onOverScrollFling(true, initialVelocity);
+                    } else {
+                        if (getChildCount() > 0) {
+                            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+                                float currentOverScrollTop = getCurrentOverScrollAmount(true);
+                                if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+                                    fling(-initialVelocity);
+                                } else {
+                                    onOverScrollFling(false, initialVelocity);
+                                }
+                            } else {
+                                if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+                                        getScrollRange())) {
+                                    animateScroll();
+                                }
+                            }
+                        }
+                    }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                if (mIsBeingDragged && getChildCount() > 0) {
+                    if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+                        animateScroll();
+                    }
+                    mActivePointerId = INVALID_POINTER;
+                    endDrag();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mLastMotionY = (int) ev.getY(index);
+                mDownX = (int) ev.getX(index);
+                mActivePointerId = ev.getPointerId(index);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP:
+                onSecondaryPointerUp(ev);
+                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+                mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
+                break;
+        }
+        return true;
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    protected boolean isInsideQsContainer(MotionEvent ev) {
+        return ev.getY() < mQsContainer.getBottom();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void onOverScrollFling(boolean open, int initialVelocity) {
+        if (mOverscrollTopChangedListener != null) {
+            mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+        }
+        mDontReportNextOverScroll = true;
+        setOverScrollAmount(0.0f, true, false);
+    }
+
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+                MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        final int pointerId = ev.getPointerId(pointerIndex);
+        if (pointerId == mActivePointerId) {
+            // This was our active pointer going up. Choose a new
+            // active pointer and adjust accordingly.
+            // TODO: Make this decision more intelligent.
+            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+            mLastMotionY = (int) ev.getY(newPointerIndex);
+            mActivePointerId = ev.getPointerId(newPointerIndex);
+            if (mVelocityTracker != null) {
+                mVelocityTracker.clear();
+            }
+        }
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void endDrag() {
+        setIsBeingDragged(false);
+
+        recycleVelocityTracker();
+
+        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+            setOverScrollAmount(0, true /* onTop */, true /* animate */);
+        }
+        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+            setOverScrollAmount(0, false /* onTop */, true /* animate */);
+        }
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
+        ev.offsetLocation(sourceView.getX(), sourceView.getY());
+        ev.offsetLocation(-targetView.getX(), -targetView.getY());
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        initDownStates(ev);
+        handleEmptySpaceClick(ev);
+        boolean expandWantsIt = false;
+        boolean swipingInProgress = mSwipeHelper.isSwipingInProgress();
+        if (!swipingInProgress && !mOnlyScrollingInThisMotion) {
+            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+        }
+        boolean scrollWantsIt = false;
+        if (!swipingInProgress && !mExpandingNotification) {
+            scrollWantsIt = onInterceptTouchEventScroll(ev);
+        }
+        boolean swipeWantsIt = false;
+        if (!mIsBeingDragged
+                && !mExpandingNotification
+                && !mExpandedInThisMotion
+                && !mOnlyScrollingInThisMotion
+                && !mDisallowDismissInThisMotion) {
+            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+        }
+        // Check if we need to clear any snooze leavebehinds
+        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+                !expandWantsIt && !scrollWantsIt) {
+            mCheckForLeavebehind = false;
+            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+        }
+        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+            mCheckForLeavebehind = true;
+        }
+        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void handleEmptySpaceClick(MotionEvent ev) {
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_MOVE:
+                if (mTouchIsClick && (Math.abs(ev.getY() - mInitialTouchY) > mTouchSlop
+                        || Math.abs(ev.getX() - mInitialTouchX) > mTouchSlop)) {
+                    mTouchIsClick = false;
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
+                        isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
+                    mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
+                }
+                break;
+        }
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private void initDownStates(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mExpandedInThisMotion = false;
+            mOnlyScrollingInThisMotion = !mScroller.isFinished();
+            mDisallowScrollingInThisMotion = false;
+            mDisallowDismissInThisMotion = false;
+            mTouchIsClick = true;
+            mInitialTouchX = ev.getX();
+            mInitialTouchY = ev.getY();
+        }
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        if (disallowIntercept) {
+            cancelLongPress();
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.INPUT)
     private boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
@@ -3710,11 +3636,6 @@
         return mIsBeingDragged;
     }
 
-    @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
-        return new StackScrollAlgorithm(context);
-    }
-
     /**
      * @return Whether the specified motion event is actually happening over the content.
      */
@@ -3723,13 +3644,6 @@
         return isInContentBounds(event.getY());
     }
 
-    /**
-     * @return Whether a y coordinate is inside the content.
-     */
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean isInContentBounds(float y) {
-        return y < getHeight() - getEmptyBottomMargin();
-    }
 
     @VisibleForTesting
     @ShadeViewRefactor(RefactorComponent.INPUT)
@@ -3742,6 +3656,83 @@
         }
     }
 
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void requestDisallowLongPress() {
+        cancelLongPress();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void requestDisallowDismiss() {
+        mDisallowDismissInThisMotion = true;
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void cancelLongPress() {
+        mSwipeHelper.cancelLongPress();
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
+        mOnEmptySpaceClickListener = listener;
+    }
+
+    /** @hide */
+    @Override
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        if (super.performAccessibilityActionInternal(action, arguments)) {
+            return true;
+        }
+        if (!isEnabled()) {
+            return false;
+        }
+        int direction = -1;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollDown:
+                direction = 1;
+                // fall through
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                // fall through
+            case android.R.id.accessibilityActionScrollUp:
+                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
+                        - mShelf.getIntrinsicHeight();
+                final int targetScrollY = Math.max(0,
+                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
+                if (targetScrollY != mOwnScrollY) {
+                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
+                    animateScroll();
+                    return true;
+                }
+                break;
+        }
+        return false;
+    }
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+        View translatingParentView = mSwipeHelper.getTranslatingParentView();
+        View view = null;
+        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+            // Only close visible guts if they're not a leavebehind.
+            view = guts;
+        } else if (menuRow != null && menuRow.isMenuVisible()
+                && translatingParentView != null) {
+            // Checking menu
+            view = translatingParentView;
+        }
+        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+            // Touch was outside visible guts / menu notification, close what's visible
+            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            resetExposedMenuView(true /* animate */, true /* force */);
+        }
+    }
+
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onWindowFocusChanged(boolean hasWindowFocus) {
@@ -3760,21 +3751,6 @@
         }
     }
 
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void requestDisallowLongPress() {
-        cancelLongPress();
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void requestDisallowDismiss() {
-        mDisallowDismissInThisMotion = true;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void cancelLongPress() {
-        mSwipeHelper.cancelLongPress();
-    }
-
     @Override
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public boolean isScrolledToTop() {
@@ -3916,7 +3892,6 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
         updateContentHeight();
         updateScrollPositionOnExpandInBottom(view);
@@ -3936,7 +3911,6 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onReset(ExpandableView view) {
         updateAnimationState(view);
         updateChronometerForChild(view);
@@ -3969,13 +3943,8 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setOnHeightChangedListener(
-            ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
-        this.mOnHeightChangedListener = mOnHeightChangedListener;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
-        mOnEmptySpaceClickListener = listener;
+            ExpandableView.OnHeightChangedListener onHeightChangedListener) {
+        this.mOnHeightChangedListener = onHeightChangedListener;
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4465,7 +4434,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setGroupManager(NotificationGroupManager groupManager) {
         this.mGroupManager = groupManager;
-        mGroupManager.setOnGroupChangeListener(this);
+        mGroupManager.setOnGroupChangeListener(mOnGroupChangeListener);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4508,33 +4477,6 @@
         return touchY > mTopPadding + mStackTranslation;
     }
 
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
-        boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
-                && (mIsExpanded || changedRow.isPinned());
-        if (animated) {
-            mExpandedGroupView = changedRow;
-            mNeedsAnimation = true;
-        }
-        changedRow.setChildrenExpanded(expanded, animated);
-        if (!mGroupExpandedForMeasure) {
-            onHeightChanged(changedRow, false /* needsAnimation */);
-        }
-        runAfterAnimationFinished(new Runnable() {
-            @Override
-            public void run() {
-                changedRow.onFinishedExpansionChange();
-            }
-        });
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
-        mStatusBar.requestNotificationUpdate();
-    }
-
     /** @hide */
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4567,46 +4509,6 @@
         info.setClassName(ScrollView.class.getName());
     }
 
-    /** @hide */
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (super.performAccessibilityActionInternal(action, arguments)) {
-            return true;
-        }
-        if (!isEnabled()) {
-            return false;
-        }
-        int direction = -1;
-        switch (action) {
-            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
-                // fall through
-            case android.R.id.accessibilityActionScrollDown:
-                direction = 1;
-                // fall through
-            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
-                // fall through
-            case android.R.id.accessibilityActionScrollUp:
-                final int viewportHeight = getHeight() - mPaddingBottom - mTopPadding - mPaddingTop
-                        - mShelf.getIntrinsicHeight();
-                final int targetScrollY = Math.max(0,
-                        Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
-                if (targetScrollY != mOwnScrollY) {
-                    mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScrollY - mOwnScrollY);
-                    animateScroll();
-                    return true;
-                }
-                break;
-        }
-        return false;
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void onGroupsChanged() {
-        mStatusBar.requestNotificationUpdate();
-    }
-
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public void generateChildOrderChangedEvent() {
         if (mIsExpanded && mAnimationsEnabled) {
@@ -4649,7 +4551,7 @@
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mHeadsUpManager.addListener(mRoundnessManager);
-        mHeadsUpManager.setAnimationStateHandler(this);
+        mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4826,7 +4728,8 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void setStatusBarState(int statusBarState) {
+    @VisibleForTesting
+    protected void setStatusBarState(int statusBarState) {
         mStatusBarState = statusBarState;
         mAmbientState.setStatusBarState(statusBarState);
     }
@@ -4912,6 +4815,34 @@
                 mMaxTopPadding,
                 mShouldShowShelfOnly ? "T" : "f",
                 mQsExpansionFraction));
+        int childCount = getChildCount();
+        pw.println("  Number of children: " + childCount);
+        pw.println();
+
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            child.dump(fd, pw, args);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                pw.println("  " + child.getClass().getSimpleName());
+                // Notifications dump it's viewstate as part of their dump to support children
+                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(
+                        child);
+                if (viewState == null) {
+                    pw.println("    no viewState!!!");
+                } else {
+                    pw.print("    ");
+                    viewState.dump(fd, pw, args);
+                    pw.println();
+                    pw.println();
+                }
+            }
+        }
+        pw.println("  Transient Views: " + childCount);
+        int transientViewCount = getTransientViewCount();
+        for (int i = 0; i < transientViewCount; i++) {
+            ExpandableView child = (ExpandableView) getTransientView(i);
+            child.dump(fd, pw, args);
+        }
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5000,7 +4931,7 @@
             return;
         }
 
-        mStatusBar.addPostCollapseAction(() -> {
+        mShadeController.addPostCollapseAction(() -> {
             setDismissAllInProgress(false);
             for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
                 if (canChildBeDismissed(rowToRemove)) {
@@ -5100,6 +5031,10 @@
         mNotificationPanel = notificationPanelView;
     }
 
+    public void updateIconAreaViews() {
+        mIconAreaController.updateNotificationIcons();
+    }
+
     /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
@@ -5140,67 +5075,7 @@
         return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
     }
 
-    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
-
-
-    /* Only ever called as a consequence of a lockscreen expansion gesture. */
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean onDraggedDown(View startingChild, int dragLengthY) {
-        if (mStatusBarState == StatusBarState.KEYGUARD
-                && hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) {
-            mLockscreenGestureLogger.write(
-                    MetricsEvent.ACTION_LS_SHADE,
-                    (int) (dragLengthY / mDisplayMetrics.density),
-                    0 /* velocityDp - N/A */);
-
-            // We have notifications, go to locked shade.
-            mStatusBar.goToLockedShade(startingChild);
-            if (startingChild instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
-                row.onExpandedByGesture(true /* drag down is always an open */);
-            }
-            return true;
-        } else {
-            // abort gesture.
-            return false;
-        }
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void onDragDownReset() {
-        setDimmed(true /* dimmed */, true /* animated */);
-        resetScrollPosition();
-        resetCheckSnoozeLeavebehind();
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void onCrossedThreshold(boolean above) {
-        setDimmed(!above /* dimmed */, true /* animate */);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void onTouchSlopExceeded() {
-        cancelLongPress();
-        checkSnoozeLeavebehind();
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void setEmptyDragAmount(float amount) {
-        mNotificationPanel.setEmptyDragAmount(amount);
-    }
-
-    @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public boolean isFalsingCheckNeeded() {
-        return mStatusBarState == StatusBarState.KEYGUARD;
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateSpeedBumpIndex() {
         int speedBumpIndex = 0;
         int currentIndex = 0;
@@ -5241,30 +5116,6 @@
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void closeControlsIfOutsideTouch(MotionEvent ev) {
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
-        View translatingParentView = mSwipeHelper.getTranslatingParentView();
-        View view = null;
-        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
-            // Only close visible guts if they're not a leavebehind.
-            view = guts;
-        } else if (menuRow != null && menuRow.isMenuVisible()
-                && translatingParentView != null) {
-            // Checking menu
-            view = translatingParentView;
-        }
-        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
-            // Touch was outside visible guts / menu notification, close what's visible
-            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
-                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            resetExposedMenuView(true /* animate */, true /* force */);
-        }
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     static class AnimationEvent {
 
@@ -5586,9 +5437,9 @@
       }
     };
 
-    class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener {
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() {
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onMenuClicked(View view, int x, int y, MenuItem item) {
             if (mLongPressListener == null) {
                 return;
@@ -5602,7 +5453,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onMenuReset(View row) {
             View translatingParentView = mSwipeHelper.getTranslatingParentView();
             if (translatingParentView != null && row == translatingParentView) {
@@ -5612,7 +5462,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onMenuShown(View row) {
             if (row instanceof ExpandableNotificationRow) {
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
@@ -5621,9 +5470,11 @@
             }
             mSwipeHelper.onMenuShown(row);
         }
-    }
+    };
 
-    class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback {
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+            new NotificationSwipeHelper.NotificationCallback() {
         @Override
         public void onDismiss() {
             mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
@@ -5643,10 +5494,8 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onDragCancelled(View v) {
             mFalsingManager.onNotificatonStopDismissing();
-            setSwipingInProgress(false);
         }
 
         /**
@@ -5654,7 +5503,6 @@
          * re-invoking dismiss logic in case the notification has not made its way out yet).
          */
         @Override
-        @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
         public void onChildDismissed(View view) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             if (!row.isDismissed()) {
@@ -5673,7 +5521,6 @@
          * @param view view (e.g. notification) to dismiss from the layout
          */
 
-        @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
         public void handleChildViewDismissed(View view) {
             if (mDismissAllInProgress) {
                 return;
@@ -5681,7 +5528,6 @@
 
             boolean isBlockingHelperShown = false;
 
-            setSwipingInProgress(false);
             if (mDragAnimPendingChildren.contains(view)) {
                 // We start the swipe and finish it in the same frame; we don't want a drag
                 // animation.
@@ -5715,13 +5561,11 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public boolean isAntiFalsingNeeded() {
             return onKeyguard();
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public View getChildAtPosition(MotionEvent ev) {
             View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
                     ev.getY());
@@ -5744,10 +5588,8 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public void onBeginDrag(View v) {
             mFalsingManager.onNotificatonStartDismissing();
-            setSwipingInProgress(true);
             mAmbientState.onBeginDrag(v);
             updateContinuousShadowDrawing();
             if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) {
@@ -5758,7 +5600,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
         public void onChildSnappedBack(View animView, float targetLeft) {
             mAmbientState.onDragFinished(animView);
             updateContinuousShadowDrawing();
@@ -5780,7 +5621,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public boolean updateSwipeProgress(View animView, boolean dismissable,
                 float swipeProgress) {
             // Returning true prevents alpha fading.
@@ -5788,7 +5628,6 @@
         }
 
         @Override
-        @ShadeViewRefactor(RefactorComponent.INPUT)
         public float getFalsingThresholdFactor() {
             return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
         }
@@ -5797,5 +5636,199 @@
         public boolean canChildBeDismissed(View v) {
             return NotificationStackScrollLayout.this.canChildBeDismissed(v);
         }
+    };
+
+    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
+
+    @ShadeViewRefactor(RefactorComponent.INPUT)
+    private final DragDownCallback mDragDownCallback = new DragDownCallback() {
+
+        /* Only ever called as a consequence of a lockscreen expansion gesture. */
+        @Override
+        public boolean onDraggedDown(View startingChild, int dragLengthY) {
+            if (mStatusBarState == StatusBarState.KEYGUARD
+                    && hasActiveNotifications()) {
+                mLockscreenGestureLogger.write(
+                        MetricsEvent.ACTION_LS_SHADE,
+                        (int) (dragLengthY / mDisplayMetrics.density),
+                        0 /* velocityDp - N/A */);
+
+                if (mNotificationPanel.onDraggedDown() || startingChild != null) {
+                    // We have notifications, go to locked shade.
+                    mShadeController.goToLockedShade(startingChild);
+                    if (startingChild instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
+                        row.onExpandedByGesture(true /* drag down is always an open */);
+                    }
+                }
+
+                return true;
+            } else {
+                // abort gesture.
+                return false;
+            }
+        }
+
+        @Override
+        public void onDragDownReset() {
+            setDimmed(true /* dimmed */, true /* animated */);
+            resetScrollPosition();
+            resetCheckSnoozeLeavebehind();
+        }
+
+        @Override
+        public void onCrossedThreshold(boolean above) {
+            setDimmed(!above /* dimmed */, true /* animate */);
+        }
+
+        @Override
+        public void onTouchSlopExceeded() {
+            cancelLongPress();
+            checkSnoozeLeavebehind();
+        }
+
+        @Override
+        public void setEmptyDragAmount(float amount) {
+            mNotificationPanel.setEmptyDragAmount(amount);
+        }
+
+        @Override
+        public boolean isFalsingCheckNeeded() {
+            return mStatusBarState == StatusBarState.KEYGUARD;
+        }
+    };
+
+    public DragDownCallback getDragDownCallback() { return mDragDownCallback; }
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() {
+        @Override
+        public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+            return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
+        }
+
+        @Override
+        public boolean isExpanded() {
+            return mIsExpanded;
+        }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
+    };
+
+    public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
+
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() {
+        @Override
+        public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
+            boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
+                    && (mIsExpanded || changedRow.isPinned());
+            if (animated) {
+                mExpandedGroupView = changedRow;
+                mNeedsAnimation = true;
+            }
+            changedRow.setChildrenExpanded(expanded, animated);
+            if (!mGroupExpandedForMeasure) {
+                onHeightChanged(changedRow, false /* needsAnimation */);
+            }
+            runAfterAnimationFinished(new Runnable() {
+                @Override
+                public void run() {
+                    changedRow.onFinishedExpansionChange();
+                }
+            });
+        }
+
+        @Override
+        public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
+            mStatusBar.requestNotificationUpdate();
+        }
+
+        @Override
+        public void onGroupsChanged() {
+            mStatusBar.requestNotificationUpdate();
+        }
+    };
+
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
+        @Override
+        public ExpandableView getChildAtPosition(float touchX, float touchY) {
+            return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY);
+        }
+
+        @Override
+        public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
+            return NotificationStackScrollLayout.this.getChildAtRawPosition(touchX, touchY);
+        }
+
+        @Override
+        public boolean canChildBeExpanded(View v) {
+            return v instanceof ExpandableNotificationRow
+                    && ((ExpandableNotificationRow) v).isExpandable()
+                    && !((ExpandableNotificationRow) v).areGutsExposed()
+                    && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
+        }
+
+        /* Only ever called as a consequence of an expansion gesture in the shade. */
+        @Override
+        public void setUserExpandedChild(View v, boolean userExpanded) {
+            if (v instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+                if (userExpanded && onKeyguard()) {
+                    // Due to a race when locking the screen while touching, a notification may be
+                    // expanded even after we went back to keyguard. An example of this happens if
+                    // you click in the empty space while expanding a group.
+
+                    // We also need to un-user lock it here, since otherwise the content height
+                    // calculated might be wrong. We also can't invert the two calls since
+                    // un-userlocking it will trigger a layout switch in the content view.
+                    row.setUserLocked(false);
+                    updateContentHeight();
+                    notifyHeightChangeListener(row);
+                    return;
+                }
+                row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
+                row.onExpandedByGesture(userExpanded);
+            }
+        }
+
+        @Override
+        public void setExpansionCancelled(View v) {
+            if (v instanceof ExpandableNotificationRow) {
+                ((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
+            }
+        }
+
+        @Override
+        public void setUserLockedChild(View v, boolean userLocked) {
+            if (v instanceof ExpandableNotificationRow) {
+                ((ExpandableNotificationRow) v).setUserLocked(userLocked);
+            }
+            cancelLongPress();
+            requestDisallowInterceptTouchEvent(true);
+        }
+
+        @Override
+        public void expansionStateChanged(boolean isExpanding) {
+            mExpandingNotification = isExpanding;
+            if (!mExpandedInThisMotion) {
+                mMaxScrollAfterExpand = mOwnScrollY;
+                mExpandedInThisMotion = true;
+            }
+        }
+
+        @Override
+        public int getMaxExpandHeight(ExpandableView view) {
+            return view.getMaxContentHeight();
+        }
+    };
+
+    public ExpandHelper.Callback getExpandHelperCallback() {
+        return mExpandHelperCallback;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 028957d..599da3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -31,11 +31,9 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
-@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT)
 class NotificationSwipeHelper extends SwipeHelper
         implements NotificationSwipeActionHelper {
     @VisibleForTesting
@@ -229,6 +227,7 @@
         if (mCallback.isExpanded()) {
             // We don't want to quick-dismiss when it's a heads up as this might lead to closing
             // of the panel early.
+            mSwipingInProgress = false;
             mCallback.handleChildViewDismissed(view);
         }
         mCallback.onDismiss();
@@ -248,6 +247,7 @@
     @Override
     public void snapChild(final View animView, final float targetLeft, float velocity) {
         superSnapChild(animView, targetLeft, velocity);
+        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         if (targetLeft == 0) {
             handleMenuCoveredOrDismissed();
@@ -354,6 +354,7 @@
 
     public void onMenuShown(View animView) {
         setExposedMenuView(getTranslatingParentView());
+        mSwipingInProgress = false;
         mCallback.onDragCancelled(animView);
         Handler handler = getHandler();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 1f3244f..a15fd70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -25,6 +25,7 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -32,12 +33,17 @@
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
 /**
  * A state of a view. This can be used to apply a set of view properties to a view with
  * {@link com.android.systemui.statusbar.notification.stack.StackScrollState} or start
  * animations with {@link com.android.systemui.statusbar.notification.stack.StackStateAnimator}.
 */
-public class ViewState {
+public class ViewState implements Dumpable {
 
     /**
      * Some animation properties that can be used to update running animations but not creating
@@ -710,4 +716,39 @@
             animator.cancel();
         }
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        StringBuilder result = new StringBuilder();
+        result.append("ViewState { ");
+
+        boolean first = true;
+        Class currentClass = this.getClass();
+        while (currentClass != null) {
+            Field[] fields = currentClass.getDeclaredFields();
+            // Print field names paired with their values
+            for (Field field : fields) {
+                int modifiers = field.getModifiers();
+                if (Modifier.isStatic(modifiers) || field.isSynthetic()
+                        || Modifier.isTransient(modifiers)) {
+                    continue;
+                }
+                if (!first) {
+                    result.append(", ");
+                }
+                try {
+                    result.append(field.getName());
+                    result.append(": ");
+                    //requires access to private field:
+                    field.setAccessible(true);
+                    result.append(field.get(this));
+                } catch (IllegalAccessException ex) {
+                }
+                first = false;
+            }
+            currentClass = currentClass.getSuperclass();
+        }
+        result.append(" }");
+        pw.print(result);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index c094669..8325bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -32,6 +32,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.NotificationMediaManager;
 
 import java.io.PrintWriter;
 
@@ -95,6 +96,8 @@
      */
     private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
 
+    private final NotificationMediaManager mMediaManager =
+            Dependency.get(NotificationMediaManager.class);
     private PowerManager mPowerManager;
     private Handler mHandler = new Handler();
     private PowerManager.WakeLock mWakeLock;
@@ -264,7 +267,7 @@
             case MODE_WAKE_AND_UNLOCK:
                 if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) {
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_PULSING");
-                    mStatusBar.updateMediaMetaData(false /* metaDataChanged */,
+                    mMediaManager.updateMediaMetaData(false /* metaDataChanged */,
                             true /* allowEnterAnimation */);
                 } else if (mMode == MODE_WAKE_AND_UNLOCK){
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 587b40d..4eca6bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -288,4 +288,10 @@
             }
         }
     }
+
+    /**
+     * Executes when button is detached from window.
+     */
+    protected void onDestroy() {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index a781be6..fa63831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -64,11 +64,12 @@
     private StatusBar mStatusBarComponent;
     private DarkIconManager mDarkIconManager;
     private View mOperatorNameFrame;
+    private CommandQueue mCommandQueue;
 
     private SignalCallback mSignalCallback = new SignalCallback() {
         @Override
         public void setIsAirplaneMode(NetworkController.IconState icon) {
-            mStatusBarComponent.recomputeDisableFlags(true /* animate */);
+            mCommandQueue.recomputeDisableFlags(true /* animate */);
         }
     };
 
@@ -78,6 +79,7 @@
         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
         mNetworkController = Dependency.get(NetworkController.class);
         mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
+        mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class);
     }
 
     @Override
@@ -116,13 +118,13 @@
     @Override
     public void onResume() {
         super.onResume();
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+        mCommandQueue.addCallbacks(this);
     }
 
     @Override
     public void onPause() {
         super.onPause();
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
+        mCommandQueue.removeCallbacks(this);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
index c7ab27b..5dded52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -18,8 +18,10 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.view.View;
+
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 
@@ -29,6 +31,9 @@
  */
 public class ContextualButton extends ButtonDispatcher {
 
+    private ContextButtonListener mListener;
+    private ContextualButtonGroup mGroup;
+
     protected final @DrawableRes int mIconResId;
 
     /**
@@ -64,6 +69,48 @@
             currentDrawable.clearAnimationCallbacks();
             currentDrawable.resetAnimation();
         }
+
+        if (mListener != null) {
+            mListener.onVisibilityChanged(this, visibility == View.VISIBLE);
+        }
+    }
+
+    public void setListener(ContextButtonListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Show this button based on its priority compared to other buttons in the group. If not
+     * attached to a group it will set its own visibility to be visible.
+     * @return if visible
+     */
+    public boolean show() {
+        if (mGroup == null) {
+            setVisibility(View.VISIBLE);
+            return true;
+        }
+        return mGroup.setButtonVisiblity(getId(), true /* visible */) == View.VISIBLE;
+    }
+
+    /**
+     * Hide this button.
+     * @return if visible
+     */
+    public boolean hide() {
+        if (mGroup == null) {
+            setVisibility(View.INVISIBLE);
+            return false;
+        }
+        return mGroup.setButtonVisiblity(getId(), false /* visible */) != View.VISIBLE;
+    }
+
+    /**
+     * Called when this button was added to the group. Keep a reference to the group to show based
+     * on priority compared to other buttons.
+     * @param group the holder of all the buttons
+     */
+    void attachToGroup(@NonNull ContextualButtonGroup group) {
+        mGroup = group;
     }
 
     protected KeyButtonDrawable getNewDrawable() {
@@ -79,4 +126,8 @@
     protected Context getContext() {
         return getCurrentView().getContext();
     }
+
+    public interface ContextButtonListener {
+        void onVisibilityChanged(ContextualButton button, boolean visible);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
index 1b03966..9703043 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButtonGroup.java
@@ -40,6 +40,7 @@
      * @param button the button added to the group
      */
     public void addButton(@NonNull ContextualButton button) {
+        button.attachToGroup(this);
         mButtonData.add(new ButtonData(button));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 4df1e3b..e4a5caa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -32,7 +32,7 @@
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
     private HeadsUpManagerPhone mHeadsUpManager;
-    private NotificationStackScrollLayout mStackScroller;
+    private Callback mCallback;
     private int mTrackingPointer;
     private float mTouchSlop;
     private float mInitialTouchX;
@@ -44,12 +44,12 @@
     private ExpandableNotificationRow mPickedChild;
 
     public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
-            NotificationStackScrollLayout stackScroller,
+            Callback callback,
             NotificationPanelView notificationPanelView) {
         mHeadsUpManager = headsUpManager;
-        mStackScroller = stackScroller;
+        mCallback = callback;
         mPanel = notificationPanelView;
-        Context context = stackScroller.getContext();
+        Context context = mCallback.getContext();
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         mTouchSlop = configuration.getScaledTouchSlop();
     }
@@ -75,13 +75,13 @@
                 mInitialTouchY = y;
                 mInitialTouchX = x;
                 setTrackingHeadsUp(false);
-                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
+                ExpandableView child = mCallback.getChildAtRawPosition(x, y);
                 mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
                     mPickedChild = (ExpandableNotificationRow) child;
-                    mTouchingHeadsUpView = !mStackScroller.isExpanded()
+                    mTouchingHeadsUpView = !mCallback.isExpanded()
                             && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
-                } else if (child == null && !mStackScroller.isExpanded()) {
+                } else if (child == null && !mCallback.isExpanded()) {
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
                     NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
@@ -174,4 +174,10 @@
         mPickedChild = null;
         mTouchingHeadsUpView = false;
     }
+
+    public interface Callback {
+        ExpandableView getChildAtRawPosition(float touchX, float touchY);
+        boolean isExpanded();
+        Context getContext();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index ae1353d..5439497 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -63,6 +63,7 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -228,6 +229,11 @@
         }
     };
 
+    public void initFrom(KeyguardBottomAreaView oldBottomArea) {
+        setKeyguardIndicationController(oldBottomArea.mIndicationController);
+        setStatusBar(oldBottomArea.mStatusBar);
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -560,7 +566,7 @@
             return;
         }
         mDarkAmount = darkAmount;
-        mIndicationArea.setAlpha(1f - darkAmount);
+        mIndicationController.setDarkAmount(darkAmount);
         mLockIcon.setDarkAmount(darkAmount);
     }
 
@@ -578,7 +584,8 @@
         }
     }
 
-    private void launchVoiceAssist() {
+    @VisibleForTesting
+    void launchVoiceAssist() {
         Runnable runnable = new Runnable() {
             @Override
             public void run() {
@@ -787,6 +794,10 @@
         mIndicationController = keyguardIndicationController;
     }
 
+    public void showTransientIndication(int id) {
+        mIndicationController.showTransientIndication(id);
+    }
+
     public void updateLeftAffordance() {
         updateLeftAffordanceIcon();
         updateLeftPreview();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 8ac8677..235629b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 33bc164..4a7bc3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -108,11 +108,7 @@
      * Dozing and receiving a notification (AOD notification.)
      */
     private boolean mPulsing;
-
-    /**
-     * Distance in pixels between the top of the screen and the first view of the bouncer.
-     */
-    private int mBouncerTop;
+    private float mEmptyDragAmount;
 
     /**
      * Refreshes the dimension values.
@@ -131,9 +127,8 @@
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
-            float panelExpansion, int parentHeight,
-            int keyguardStatusHeight, float dark, boolean secure, boolean pulsing,
-            int bouncerTop) {
+            float panelExpansion, int parentHeight, int keyguardStatusHeight, float dark,
+            boolean secure, boolean pulsing, float emptyDragAmount) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
@@ -143,14 +138,14 @@
         mDarkAmount = dark;
         mCurrentlySecure = secure;
         mPulsing = pulsing;
-        mBouncerTop = bouncerTop;
+        mEmptyDragAmount = emptyDragAmount;
     }
 
     public void run(Result result) {
         final int y = getClockY();
         result.clockY = y;
         result.clockAlpha = getClockAlpha(y);
-        result.stackScrollerPadding = y + (mPulsing ? 0 : mKeyguardStatusHeight);
+        result.stackScrollerPadding = y + (mPulsing ? mPulsingPadding : mKeyguardStatusHeight);
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
@@ -194,15 +189,14 @@
         }
 
         float clockYRegular = getExpandedClockPosition();
-        boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
-        float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
-                mMinTopMargin : -mKeyguardStatusHeight;
+        float clockYBouncer = -mKeyguardStatusHeight;
 
         // Move clock up while collapsing the shade
         float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(mPanelExpansion);
-        final float clockY = MathUtils.lerp(clockYTarget, clockYRegular, shadeExpansion);
+        float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion);
+        clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion);
 
-        return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
+        return (int) (MathUtils.lerp(clockY, clockYDark, mDarkAmount) + mEmptyDragAmount);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
index 76ddca4..6111178 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
@@ -16,9 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.annotation.Nullable;
-
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
 
 /** Executes actions that require the screen to be unlocked. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index d676692..462201c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -18,7 +18,7 @@
 
 import android.util.Log;
 
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
 /**
  * Executes actions that require the screen to be unlocked. Delegates the actual handling to an
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
new file mode 100644
index 0000000..b3423a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
+import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
+
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
+
+    private static final String TAG = "KeyguardEnvironmentImpl";
+
+    private final NotificationLockscreenUserManager mLockscreenUserManager =
+            Dependency.get(NotificationLockscreenUserManager.class);
+    private final DeviceProvisionedController mDeviceProvisionedController =
+            Dependency.get(DeviceProvisionedController.class);
+    private final NotificationMediaManager mMediaManager =
+            Dependency.get(NotificationMediaManager.class);
+
+    public KeyguardEnvironmentImpl() {
+    }
+
+    @Override  // NotificationData.KeyguardEnvironment
+    public boolean isDeviceProvisioned() {
+        return mDeviceProvisionedController.isDeviceProvisioned();
+    }
+
+    @Override  // NotificationData.KeyguardEnvironment
+    public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
+        final int notificationUserId = n.getUserId();
+        if (DEBUG && MULTIUSER_DEBUG) {
+            Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n,
+                    mLockscreenUserManager.getCurrentUserId(), notificationUserId));
+        }
+        return mLockscreenUserManager.isCurrentProfile(notificationUserId);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 40ddf5b..673cdb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -40,6 +40,8 @@
 import android.util.Log;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationMediaManager;
 
 import libcore.io.IoUtils;
 
@@ -52,6 +54,9 @@
 
     private static final String TAG = "LockscreenWallpaper";
 
+    private final NotificationMediaManager mMediaManager =
+            Dependency.get(NotificationMediaManager.class);
+
     private final StatusBar mBar;
     private final WallpaperManager mWallpaperManager;
     private final Handler mH;
@@ -193,7 +198,7 @@
                     mCached = true;
                     mCache = result.bitmap;
                     mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
-                    mBar.updateMediaMetaData(
+                    mMediaManager.updateMediaMetaData(
                             true /* metaDataChanged */, true /* allowEnterAnimation */);
                 }
                 mLoader = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 6bccf31..59c15f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -19,26 +19,20 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 
-import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
-
+import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
-import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
 import android.annotation.IdRes;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.Fragment;
-import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
@@ -55,11 +49,9 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
-import androidx.annotation.VisibleForTesting;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -72,16 +64,16 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
@@ -90,21 +82,20 @@
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.KeyButtonDrawable;
-import com.android.systemui.statusbar.policy.KeyButtonView;
-import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyButtonView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
 import java.util.Locale;
-import java.util.Optional;
+import java.util.function.Consumer;
 
 /**
  * Fragment containing the NavigationBarFragment. Contains logic for what happens
@@ -114,18 +105,15 @@
 
     public static final String TAG = "NavigationBar";
     private static final boolean DEBUG = false;
-    private static final boolean DEBUG_ROTATION = true;
     private static final String EXTRA_DISABLE_STATE = "disabled_state";
     private static final String EXTRA_DISABLE2_STATE = "disabled2_state";
 
-    private final static int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
-    private final static int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000;
-
-    private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
-
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
+    private final DeviceProvisionedController mDeviceProvisionedController =
+            Dependency.get(DeviceProvisionedController.class);
+
     protected NavigationBarView mNavigationBarView = null;
     protected AssistManager mAssistManager;
 
@@ -133,7 +121,6 @@
 
     private int mNavigationIconHints = 0;
     private int mNavigationBarMode;
-    private boolean mAccessibilityFeedbackEnabled;
     private AccessibilityManager mAccessibilityManager;
     private MagnificationContentObserver mMagnificationObserver;
     private ContentResolver mContentResolver;
@@ -158,18 +145,6 @@
 
     public boolean mHomeBlockedThisTouch;
 
-    private int mLastRotationSuggestion;
-    private boolean mPendingRotationSuggestion;
-    private boolean mHoveringRotationSuggestion;
-    private RotationLockController mRotationLockController;
-    private TaskStackListenerImpl mTaskStackListener;
-
-    private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
-    private final Runnable mCancelPendingRotationProposal =
-            () -> mPendingRotationSuggestion = false;
-    private Animator mRotateHideAnimator;
-    private ViewRippler mViewRippler = new ViewRippler();
-
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
         @Override
         public void onConnectionChanged(boolean isConnected) {
@@ -180,7 +155,7 @@
         @Override
         public void onQuickStepStarted() {
             // Use navbar dragging as a signal to hide the rotate button
-            setRotateSuggestionButtonState(false);
+            mNavigationBarView.getRotateSuggestionButton().setRotateSuggestionButtonState(false);
 
             // Hide the notifications panel when quick step starts
             mStatusBar.collapsePanel(true /* animate */);
@@ -206,6 +181,17 @@
         }
     };
 
+    private final ContextButtonListener mRotationButtonListener = new ContextButtonListener() {
+        @Override
+        public void onVisibilityChanged(ContextualButton button, boolean visible) {
+            if (visible) {
+                // If the button will actually become visible and the navbar is about to hide,
+                // tell the statusbar to keep it around for longer
+                mStatusBar.touchAutoHide();
+            }
+        }
+    };
+
     // ----- Fragment Lifecycle Callbacks -----
 
     @Override
@@ -233,26 +219,6 @@
         }
         mAssistManager = Dependency.get(AssistManager.class);
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
-
-        try {
-            WindowManagerGlobal.getWindowManagerService()
-                    .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        mRotationLockController = Dependency.get(RotationLockController.class);
-
-        // Reset user rotation pref to match that of the WindowManager if starting in locked mode
-        // This will automatically happen when switching from auto-rotate to locked mode
-        if (mRotationLockController.isRotationLocked()) {
-            final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
-            mRotationLockController.setRotationLockedAtAngle(true, winRotation);
-        }
-
-        // Register the task stack listener
-        mTaskStackListener = new TaskStackListenerImpl();
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
     }
 
     @Override
@@ -262,15 +228,6 @@
         Dependency.get(AccessibilityManagerWrapper.class).removeCallback(
                 mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mMagnificationObserver);
-        try {
-            WindowManagerGlobal.getWindowManagerService()
-                    .removeRotationWatcher(mRotationWatcher);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        // Unregister the task stack listener
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
     }
 
     @Override
@@ -284,8 +241,8 @@
         super.onViewCreated(view, savedInstanceState);
         mNavigationBarView = (NavigationBarView) view;
 
-        mNavigationBarView.setDisabledFlags(mDisabledFlags1);
         mNavigationBarView.setComponents(mStatusBar.getPanel());
+        mNavigationBarView.setDisabledFlags(mDisabledFlags1);
         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
         mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
         if (savedInstanceState != null) {
@@ -303,6 +260,17 @@
         getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
         notifyNavigationBarScreenOn();
         mOverviewProxyService.addCallback(mOverviewProxyListener);
+
+        RotationContextButton rotationButton = mNavigationBarView.getRotateSuggestionButton();
+        rotationButton.setListener(mRotationButtonListener);
+        rotationButton.addRotationCallback(mRotationWatcher);
+
+        // Reset user rotation pref to match that of the WindowManager if starting in locked mode
+        // This will automatically happen when switching from auto-rotate to locked mode
+        if (rotationButton.isRotationLocked()) {
+            final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
+            rotationButton.setRotationLockedAtAngle(winRotation);
+        }
     }
 
     @Override
@@ -413,229 +381,31 @@
             mNavigationBarWindowState = state;
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
 
-            // If the navbar is visible, show the rotate button if there's a pending suggestion
-            if (state == WINDOW_STATE_SHOWING && mPendingRotationSuggestion) {
-                showAndLogRotationSuggestion();
-            }
+            mNavigationBarView.getRotateSuggestionButton()
+                    .onNavigationBarWindowVisibilityChange(state == WINDOW_STATE_SHOWING);
         }
     }
 
     @Override
     public void onRotationProposal(final int rotation, boolean isValid) {
         final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
-        final boolean rotateSuggestionsDisabled = hasDisable2RotateSuggestionFlag(mDisabledFlags2);
-        if (DEBUG_ROTATION) {
+        final boolean rotateSuggestionsDisabled = RotationContextButton
+                .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
+        if (RotationContextButton.DEBUG_ROTATION) {
             Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
                     + ", winRotation=" + Surface.rotationToString(winRotation)
                     + ", isValid=" + isValid + ", mNavBarWindowState="
                     + StatusBarManager.windowStateToString(mNavigationBarWindowState)
                     + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
                     + ", isRotateButtonVisible=" + (mNavigationBarView == null ? "null" :
-                        mNavigationBarView.isRotateButtonVisible()));
+                        mNavigationBarView.getRotateSuggestionButton().isVisible()));
         }
 
         // Respect the disabled flag, no need for action as flag change callback will handle hiding
         if (rotateSuggestionsDisabled) return;
 
-        // This method will be called on rotation suggestion changes even if the proposed rotation
-        // is not valid for the top app. Use invalid rotation choices as a signal to remove the
-        // rotate button if shown.
-        if (!isValid) {
-            setRotateSuggestionButtonState(false);
-            return;
-        }
-
-        // If window rotation matches suggested rotation, remove any current suggestions
-        if (rotation == winRotation) {
-            getView().removeCallbacks(mRemoveRotationProposal);
-            setRotateSuggestionButtonState(false);
-            return;
-        }
-
-        // Prepare to show the navbar icon by updating the icon style to change anim params
-        mLastRotationSuggestion = rotation; // Remember rotation for click
-        if (mNavigationBarView != null) {
-            final boolean rotationCCW = isRotationAnimationCCW(winRotation, rotation);
-            int style;
-            if (winRotation == Surface.ROTATION_0 || winRotation == Surface.ROTATION_180) {
-                style = rotationCCW ? R.style.RotateButtonCCWStart90 :
-                        R.style.RotateButtonCWStart90;
-            } else { // 90 or 270
-                style = rotationCCW ? R.style.RotateButtonCCWStart0 :
-                        R.style.RotateButtonCWStart0;
-            }
-            mNavigationBarView.updateRotateSuggestionButtonStyle(style);
-        }
-
-        if (mNavigationBarWindowState != WINDOW_STATE_SHOWING) {
-            // If the navbar isn't shown, flag the rotate icon to be shown should the navbar become
-            // visible given some time limit.
-            mPendingRotationSuggestion = true;
-            getView().removeCallbacks(mCancelPendingRotationProposal);
-            getView().postDelayed(mCancelPendingRotationProposal,
-                    NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS);
-
-        } else { // The navbar is visible so show the icon right away
-            showAndLogRotationSuggestion();
-        }
-    }
-
-    private void onRotationSuggestionsDisabled() {
-        // Immediately hide the rotate button and clear any planned removal
-        setRotateSuggestionButtonState(false, true);
-
-        // This method can be called before view setup is done, ensure getView isn't null
-        final View v = getView();
-        if (v != null) v.removeCallbacks(mRemoveRotationProposal);
-    }
-
-    private void showAndLogRotationSuggestion() {
-        setRotateSuggestionButtonState(true);
-        rescheduleRotationTimeout(false);
-        mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN);
-    }
-
-    private boolean isRotationAnimationCCW(int from, int to) {
-        // All 180deg WM rotation animations are CCW, match that
-        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
-        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
-        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
-        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
-        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
-        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
-        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
-        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
-        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
-        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
-        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
-        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
-        return false; // Default
-    }
-
-    public void setRotateSuggestionButtonState(final boolean visible) {
-        setRotateSuggestionButtonState(visible, false);
-    }
-
-    public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
-        if (mNavigationBarView == null) return;
-
-        // At any point the the button can become invisible because an a11y service became active.
-        // Similarly, a call to make the button visible may be rejected because an a11y service is
-        // active. Must account for this.
-
-        ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
-        final boolean currentlyVisible = mNavigationBarView.isRotateButtonVisible();
-
-        // Rerun a show animation to indicate change but don't rerun a hide animation
-        if (!visible && !currentlyVisible) return;
-
-        View view = rotBtn.getCurrentView();
-        if (view == null) return;
-
-        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
-        if (kbd == null) return;
-
-        // Clear any pending suggestion flag as it has either been nullified or is being shown
-        mPendingRotationSuggestion = false;
-        if (getView() != null) getView().removeCallbacks(mCancelPendingRotationProposal);
-
-        // Handle the visibility change and animation
-        if (visible) { // Appear and change (cannot force)
-            // Stop and clear any currently running hide animations
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
-                mRotateHideAnimator.cancel();
-            }
-            mRotateHideAnimator = null;
-
-            // Reset the alpha if any has changed due to hide animation
-            view.setAlpha(1f);
-
-            // Run the rotate icon's animation if it has one
-            if (kbd.canAnimate()) {
-                kbd.resetAnimation();
-                kbd.startAnimation();
-            }
-
-            if (!isRotateSuggestionIntroduced()) mViewRippler.start(view);
-
-            // Set visibility, may fail if a11y service is active.
-            // If invisible, call will stop animation.
-            int appliedVisibility = mNavigationBarView.setRotateButtonVisibility(true);
-            if (appliedVisibility == View.VISIBLE) {
-                // If the button will actually become visible and the navbar is about to hide,
-                // tell the statusbar to keep it around for longer
-                mStatusBar.touchAutoHide();
-            }
-
-        } else { // Hide
-
-            mViewRippler.stop(); // Prevent any pending ripples, force hide or not
-
-            if (force) {
-                // If a hide animator is running stop it and make invisible
-                if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
-                    mRotateHideAnimator.pause();
-                }
-                mNavigationBarView.setRotateButtonVisibility(false);
-                return;
-            }
-
-            // Don't start any new hide animations if one is running
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
-
-            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha",
-                    0f);
-            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
-            fadeOut.setInterpolator(Interpolators.LINEAR);
-            fadeOut.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mNavigationBarView.setRotateButtonVisibility(false);
-                }
-            });
-
-            mRotateHideAnimator = fadeOut;
-            fadeOut.start();
-        }
-    }
-
-    private void rescheduleRotationTimeout(final boolean reasonHover) {
-        // May be called due to a new rotation proposal or a change in hover state
-        if (reasonHover) {
-            // Don't reschedule if a hide animator is running
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
-            // Don't reschedule if not visible
-            if (!mNavigationBarView.isRotateButtonVisible()) return;
-        }
-
-        getView().removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
-        getView().postDelayed(mRemoveRotationProposal,
-                computeRotationProposalTimeout()); // Schedule timeout
-    }
-
-    private int computeRotationProposalTimeout() {
-        if (mAccessibilityFeedbackEnabled) return 20000;
-        if (mHoveringRotationSuggestion) return 16000;
-        return 10000;
-    }
-
-    private boolean isRotateSuggestionIntroduced() {
-        ContentResolver cr = getContext().getContentResolver();
-        return Settings.Secure.getInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0)
-                >= NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION;
-    }
-
-    private void incrementNumAcceptedRotationSuggestionsIfNeeded() {
-        // Get the number of accepted suggestions
-        ContentResolver cr = getContext().getContentResolver();
-        final int numSuggestions = Settings.Secure.getInt(cr,
-                Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0);
-
-        // Increment the number of accepted suggestions only if it would change intro mode
-        if (numSuggestions < NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION) {
-            Settings.Secure.putInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
-                    numSuggestions + 1);
-        }
+        mNavigationBarView.getRotateSuggestionButton()
+                .onRotationProposal(rotation, winRotation, isValid);
     }
 
     // Injected from StatusBar at creation.
@@ -708,12 +478,9 @@
 
     private void setDisabled2Flags(int state2) {
         // Method only called on change of disable2 flags
-        final boolean rotateSuggestionsDisabled = hasDisable2RotateSuggestionFlag(state2);
-        if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled();
-    }
-
-    private boolean hasDisable2RotateSuggestionFlag(int disable2Flags) {
-        return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getRotateSuggestionButton().onDisable2FlagChanged(state2);
+        }
     }
 
     // ----- Internal stuffz -----
@@ -725,7 +492,7 @@
     }
 
     private boolean shouldDisableNavbarGestures() {
-        return !mStatusBar.isDeviceProvisioned()
+        return !mDeviceProvisionedController.isDeviceProvisioned()
                 || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
     }
 
@@ -778,9 +545,6 @@
         accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
         updateAccessibilityServicesState(mAccessibilityManager);
 
-        ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
-        rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
-        rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
         updateScreenPinningGestures();
     }
 
@@ -1000,27 +764,14 @@
             }
         }
 
-        mAccessibilityFeedbackEnabled = feedbackEnabled;
+        mNavigationBarView.getRotateSuggestionButton()
+                .setAccessibilityFeedbackEnabled(feedbackEnabled);
 
         final boolean showAccessibilityButton = requestingServices >= 1;
         final boolean targetSelection = requestingServices >= 2;
         mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
     }
 
-    private void onRotateSuggestionClick(View v) {
-        mMetricsLogger.action(MetricsEvent.ACTION_ROTATION_SUGGESTION_ACCEPTED);
-        incrementNumAcceptedRotationSuggestionsIfNeeded();
-        mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
-    }
-
-    private boolean onRotateSuggestionHover(View v, MotionEvent event) {
-        final int action = event.getActionMasked();
-        mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
-                || (action == MotionEvent.ACTION_HOVER_MOVE);
-        rescheduleRotationTimeout(true);
-        return false; // Must return false so a11y hover events are dispatched correctly.
-    }
-
     // ----- Methods that StatusBar talks to (should be minimized) -----
 
     public void setLightBarController(LightBarController lightBarController) {
@@ -1066,36 +817,10 @@
         }
     }
 
-    private final Stub mRotationWatcher = new Stub() {
-        @Override
-        public void onRotationChanged(final int rotation) throws RemoteException {
-            // We need this to be scheduled as early as possible to beat the redrawing of
-            // window in response to the orientation change.
-            Handler h = getView().getHandler();
-            Message msg = Message.obtain(h, () -> {
-
-                // If the screen rotation changes while locked, potentially update lock to flow with
-                // new screen rotation and hide any showing suggestions.
-                if (mRotationLockController.isRotationLocked()) {
-                    if (shouldOverrideUserLockPrefs(rotation)) {
-                        mRotationLockController.setRotationLockedAtAngle(true, rotation);
-                    }
-                    setRotateSuggestionButtonState(false, true);
-                }
-
-                if (mNavigationBarView != null
-                        && mNavigationBarView.needsReorient(rotation)) {
-                    repositionNavigationBar();
-                }
-            });
-            msg.setAsynchronous(true);
-            h.sendMessageAtFrontOfQueue(msg);
-        }
-
-        private boolean shouldOverrideUserLockPrefs(final int rotation) {
-            // Only override user prefs when returning to the natural rotation (normally portrait).
-            // Don't let apps that force landscape or 180 alter user lock.
-            return rotation == NATURAL_ROTATION;
+    private final Consumer<Integer> mRotationWatcher = rotation -> {
+        if (mNavigationBarView != null
+                && mNavigationBarView.needsReorient(rotation)) {
+            repositionNavigationBar();
         }
     };
 
@@ -1114,69 +839,6 @@
         }
     };
 
-    class TaskStackListenerImpl extends TaskStackChangeListener {
-        // Invalidate any rotation suggestion on task change or activity orientation change
-        // Note: all callbacks happen on main thread
-
-        @Override
-        public void onTaskStackChanged() {
-            setRotateSuggestionButtonState(false);
-        }
-
-        @Override
-        public void onTaskRemoved(int taskId) {
-            setRotateSuggestionButtonState(false);
-        }
-
-        @Override
-        public void onTaskMovedToFront(int taskId) {
-            setRotateSuggestionButtonState(false);
-        }
-
-        @Override
-        public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
-            // Only hide the icon if the top task changes its requestedOrientation
-            // Launcher can alter its requestedOrientation while it's not on top, don't hide on this
-            Optional.ofNullable(ActivityManagerWrapper.getInstance())
-                    .map(ActivityManagerWrapper::getRunningTask)
-                    .ifPresent(a -> {
-                        if (a.id == taskId) setRotateSuggestionButtonState(false);
-                    });
-        }
-    }
-
-    private class ViewRippler {
-        private static final int RIPPLE_OFFSET_MS = 50;
-        private static final int RIPPLE_INTERVAL_MS = 2000;
-        private View mRoot;
-
-        public void start(View root) {
-            stop(); // Stop any pending ripple animations
-
-            mRoot = root;
-
-            // Schedule pending ripples, offset the 1st to avoid problems with visibility change
-            mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
-            mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
-            mRoot.postOnAnimationDelayed(mRipple, 2*RIPPLE_INTERVAL_MS);
-            mRoot.postOnAnimationDelayed(mRipple, 3*RIPPLE_INTERVAL_MS);
-            mRoot.postOnAnimationDelayed(mRipple, 4*RIPPLE_INTERVAL_MS);
-        }
-
-        public void stop() {
-            if (mRoot != null) mRoot.removeCallbacks(mRipple);
-        }
-
-        private final Runnable mRipple = new Runnable() {
-            @Override
-            public void run() { // Cause the ripple to fire via false presses
-                if (!mRoot.isAttachedToWindow()) return;
-                mRoot.setPressed(true);
-                mRoot.setPressed(false);
-            }
-        };
-    }
-
     public static View create(Context context, FragmentListener listener) {
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index c4efa94..2ab5958 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -29,7 +29,6 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.DrawableRes;
-import android.annotation.StyleRes;
 import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -60,7 +59,6 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
-import com.android.systemui.RecentsComponent;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -71,7 +69,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
@@ -140,6 +137,7 @@
     private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
     private final ContextualButtonGroup mContextualButtonGroup;
     private Configuration mConfiguration;
+    private Configuration mTmpLastConfiguration;
 
     private NavigationBarInflaterView mNavigationInflaterView;
     private RecentsOnboarding mRecentsOnboarding;
@@ -272,7 +270,7 @@
         final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
                 R.drawable.ic_ime_switcher_default);
         final RotationContextButton rotateSuggestionButton = new RotationContextButton(
-                R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button,
+                R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button, getContext(),
                 R.style.RotateButtonCCWStart90);
         final ContextualButton accessibilityButton =
                 new ContextualButton(R.id.accessibility_button,
@@ -286,6 +284,7 @@
         mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
 
         mConfiguration = new Configuration();
+        mTmpLastConfiguration = new Configuration();
         mConfiguration.updateFrom(context.getResources().getConfiguration());
 
         mScreenPinningNotify = new ScreenPinningNotify(mContext);
@@ -350,7 +349,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         shouldDeadZoneConsumeTouchEvents(event);
-        if (mGestureHelper.onTouchEvent(event)) {
+        if (mGestureHelper != null && mGestureHelper.onTouchEvent(event)) {
             return true;
         }
         return super.onTouchEvent(event);
@@ -418,8 +417,9 @@
         return mButtonDispatchers.get(R.id.accessibility_button);
     }
 
-    public ButtonDispatcher getRotateSuggestionButton() {
-        return mButtonDispatchers.get(R.id.rotate_suggestion);
+    public RotationContextButton getRotateSuggestionButton() {
+        return (RotationContextButton) mContextualButtonGroup
+                .getContextButton(R.id.rotate_suggestion);
     }
 
     public SparseArray<ButtonDispatcher> getButtonDispatchers() {
@@ -445,13 +445,13 @@
     }
 
     private void reloadNavIcons() {
-        updateIcons(Configuration.EMPTY, mConfiguration);
+        updateIcons(Configuration.EMPTY);
     }
 
-    private void updateIcons(Configuration oldConfig, Configuration newConfig) {
-        final boolean orientationChange = oldConfig.orientation != newConfig.orientation;
-        final boolean densityChange = oldConfig.densityDpi != newConfig.densityDpi;
-        final boolean dirChange = oldConfig.getLayoutDirection() != newConfig.getLayoutDirection();
+    private void updateIcons(Configuration oldConfig) {
+        final boolean orientationChange = oldConfig.orientation != mConfiguration.orientation;
+        final boolean densityChange = oldConfig.densityDpi != mConfiguration.densityDpi;
+        final boolean dirChange = oldConfig.getLayoutDirection() != mConfiguration.getLayoutDirection();
 
         if (orientationChange || densityChange) {
             mDockedIcon = getDrawable(R.drawable.ic_sysbar_docked);
@@ -485,7 +485,7 @@
     private void orientBackButton(KeyButtonDrawable drawable) {
         final boolean useAltBack =
                 (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
-        final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
         float degrees = useAltBack
                 ? (isRtl ? 270 : -90)
                 : (isRtl ? 180 : 0);
@@ -680,7 +680,9 @@
     }
 
     public void onNavigationButtonLongPress(View v) {
-        mGestureHelper.onNavigationButtonLongPress(v);
+        if (mGestureHelper != null) {
+            mGestureHelper.onNavigationButtonLongPress(v);
+        }
     }
 
     public void onPanelExpandedChange(boolean expanded) {
@@ -744,27 +746,12 @@
         mContextualButtonGroup.setButtonVisiblity(R.id.menu, show);
     }
 
-    public void updateRotateSuggestionButtonStyle(@StyleRes int style) {
-        RotationContextButton button = (RotationContextButton) mContextualButtonGroup
-                .getContextButton(R.id.rotate_suggestion);
-        button.setStyle(style);
-        button.updateIcon();
-    }
-
     public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
         mLongClickableAccessibilityButton = longClickable;
         getAccessibilityButton().setLongClickable(longClickable);
         mContextualButtonGroup.setButtonVisiblity(R.id.accessibility_button, visible);
     }
 
-    public int setRotateButtonVisibility(boolean visible) {
-        return mContextualButtonGroup.setButtonVisiblity(R.id.rotate_suggestion, visible);
-    }
-
-    public boolean isRotateButtonVisible() {
-        return getRotateSuggestionButton().isVisible();
-    }
-
     void hideRecentsOnboarding() {
         mRecentsOnboarding.hide(true);
     }
@@ -807,7 +794,9 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        mGestureHelper.onDraw(canvas);
+        if (mGestureHelper != null) {
+            mGestureHelper.onDraw(canvas);
+        }
         mDeadZone.onDraw(canvas);
         super.onDraw(canvas);
     }
@@ -819,7 +808,9 @@
         updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
         updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds);
         updateButtonLocationOnScreen(getRotateSuggestionButton(), mRotationButtonBounds);
-        mGestureHelper.onLayout(changed, left, top, right, bottom);
+        if (mGestureHelper != null) {
+            mGestureHelper.onLayout(changed, left, top, right, bottom);
+        }
         mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
     }
 
@@ -946,26 +937,27 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        boolean uiCarModeChanged = updateCarMode(newConfig);
+        mTmpLastConfiguration.updateFrom(mConfiguration);
+        mConfiguration.updateFrom(newConfig);
+        boolean uiCarModeChanged = updateCarMode();
         updateTaskSwitchHelper();
-        updateIcons(mConfiguration, newConfig);
+        updateIcons(mTmpLastConfiguration);
         updateRecentsIcon();
-        mRecentsOnboarding.onConfigurationChanged(newConfig);
-        if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi
-                || mConfiguration.getLayoutDirection() != newConfig.getLayoutDirection()) {
+        mRecentsOnboarding.onConfigurationChanged(mConfiguration);
+        if (uiCarModeChanged || mTmpLastConfiguration.densityDpi != mConfiguration.densityDpi
+                || mTmpLastConfiguration.getLayoutDirection() != mConfiguration.getLayoutDirection()) {
             // If car mode or density changes, we need to reset the icons.
             updateNavButtonIcons();
         }
-        mConfiguration.updateFrom(newConfig);
     }
 
     /**
      * If the configuration changed, update the carmode and return that it was updated.
      */
-    private boolean updateCarMode(Configuration newConfig) {
+    private boolean updateCarMode() {
         boolean uiCarModeChanged = false;
-        if (newConfig != null) {
-            int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
+        if (mConfiguration != null) {
+            int uiMode = mConfiguration.uiMode & Configuration.UI_MODE_TYPE_MASK;
             final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
 
             if (isCarMode != mInCarMode) {
@@ -1051,6 +1043,9 @@
             mGestureHelper.destroy();
         }
         setUpSwipeUpOnboarding(false);
+        for (int i = 0; i < mButtonDispatchers.size(); ++i) {
+            mButtonDispatchers.valueAt(i).onDestroy();
+        }
     }
 
     private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) {
@@ -1100,10 +1095,11 @@
                         visibilityToString(getCurrentView().getVisibility()),
                         getCurrentView().getAlpha()));
 
-        pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s",
+        pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f",
                         mDisabledFlags,
                         mVertical ? "true" : "false",
-                        getMenuButton().isVisible() ? "true" : "false"));
+                        getMenuButton().isVisible() ? "true" : "false",
+                        getLightTransitionsController().getCurrentDarkIntensity()));
 
         dumpButton(pw, "back", getBackButton());
         dumpButton(pw, "home", getHomeButton());
@@ -1115,7 +1111,9 @@
         pw.println("    }");
 
         mContextualButtonGroup.dump(pw);
-        mGestureHelper.dump(pw);
+        if (mGestureHelper != null) {
+            mGestureHelper.dump(pw);
+        }
         mRecentsOnboarding.dump(pw);
     }
 
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 b19f57d0..21b98db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -49,7 +49,6 @@
     private ViewGroup mNotificationScrollLayout;
     private Context mContext;
     private boolean mFullyDark;
-    private boolean mHasShelfIconsWhenFullyDark;
 
     public NotificationIconAreaController(Context context, StatusBar statusBar) {
         mStatusBar = statusBar;
@@ -176,33 +175,10 @@
 
         updateStatusBarIcons();
         updateShelfIcons();
-        updateHasShelfIconsWhenFullyDark();
 
         applyNotificationIconsTint();
     }
 
-    private void updateHasShelfIconsWhenFullyDark() {
-        boolean hasIconsWhenFullyDark = false;
-        for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
-            View view = mNotificationScrollLayout.getChildAt(i);
-            if (view instanceof ExpandableNotificationRow) {
-                NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
-                if (shouldShowNotificationIcon(ent,
-                        NotificationShelf.SHOW_AMBIENT_ICONS /* showAmbient */,
-                        false /* hideDismissed */,
-                        true /* hideReplied */)) {
-                    hasIconsWhenFullyDark = true;
-                    break;
-                }
-            }
-        }
-        mHasShelfIconsWhenFullyDark = hasIconsWhenFullyDark;
-    }
-
-    public boolean hasShelfIconsWhenFullyDark() {
-        return mHasShelfIconsWhenFullyDark;
-    }
-
     private void updateShelfIcons() {
         updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
                 NotificationShelf.SHOW_AMBIENT_ICONS, false /* hideDismissed */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 6d53cd3..31facb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.SysUiServiceProvider.getComponent;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
         .ExpandAnimationParameters;
 
@@ -38,6 +39,7 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
@@ -64,10 +66,12 @@
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSFragment;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -104,6 +108,11 @@
 
     private static final boolean DEBUG = false;
 
+    private static final boolean EXPAND_ON_WAKE_UP = SystemProperties.getBoolean(
+            "persist.sysui.expand_shade_on_wake_up", true);
+    private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
+            "persist.sysui.go_to_shade_on_wake_up", true);
+
     /**
      * Fling expanding QS.
      */
@@ -280,6 +289,12 @@
      */
     private float mLinearDarkAmount;
 
+    /**
+     * State where the device isn't dozing anymore, but the lock screen isn't fully awake.
+     * The screen will be dimmed down with the shade collapsed.
+     */
+    private boolean mSemiAwake;
+
     private float mDarkAmountTarget;
     private boolean mPulsing;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -325,6 +340,11 @@
             Dependency.get(NotificationEntryManager.class);
 
     private final StateListener mListener = this::setBarState;
+    private final CommandQueue mCommandQueue;
+    private final NotificationLockscreenUserManager mLockscreenUserManager =
+            Dependency.get(NotificationLockscreenUserManager.class);
+    private final ShadeController mShadeController =
+            Dependency.get(ShadeController.class);
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -335,6 +355,7 @@
         setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
         mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
         setPanelAlpha(255, false /* animate */);
+        mCommandQueue = getComponent(context, CommandQueue.class);
     }
 
     private void setStatusBar(StatusBar bar) {
@@ -446,10 +467,12 @@
         // Update keyguard bottom area
         index = indexOfChild(mKeyguardBottomArea);
         removeView(mKeyguardBottomArea);
+        KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
         mKeyguardBottomArea = (KeyguardBottomAreaView) LayoutInflater.from(mContext).inflate(
                 R.layout.keyguard_bottom_area,
                 this,
                 false);
+        mKeyguardBottomArea.initFrom(oldBottomArea);
         addView(mKeyguardBottomArea, index);
         initBottomArea();
         setDarkAmount(mLinearDarkAmount, mInterpolatedDarkAmount);
@@ -573,7 +596,7 @@
                     mInterpolatedDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
                     mPulsing,
-                    mBouncerTop);
+                    mEmptyDragAmount);
             mClockPositionAlgorithm.run(mClockPositionResult);
             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
                     mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
@@ -618,7 +641,7 @@
             if (suppressedSummary) {
                 continue;
             }
-            if (!mStatusBar.getNotificationLockscreenUserManager().shouldShowOnKeyguard(
+            if (!mLockscreenUserManager.shouldShowOnKeyguard(
                     row.getStatusBarNotification())) {
                 continue;
             }
@@ -1235,6 +1258,12 @@
         if (keyguardShowing) {
             updateDozingVisibilities(false /* animate */);
         }
+
+        // Expand notification shade if the device was is semi-awake state
+        if (mBarState == StatusBarState.SHADE && isSemiAwake()) {
+            mNotificationStackScroller.setDark(false /* dark */, false /* animated */,
+                    null /* touchLocation */);
+        }
         resetVerticalPanelPosition();
         updateQsState();
     }
@@ -2335,13 +2364,7 @@
     }
 
     public void setEmptyDragAmount(float amount) {
-        float factor = 0.8f;
-        if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
-            factor = 0.4f;
-        } else if (!mStatusBar.hasActiveNotifications()) {
-            factor = 0.4f;
-        }
-        mEmptyDragAmount = amount * factor;
+        mEmptyDragAmount = amount * 0.2f;
         positionClockAndNotifications();
     }
 
@@ -2402,7 +2425,7 @@
                 return true;
             case StatusBarState.SHADE_LOCKED:
                 if (!mQsExpanded) {
-                    mStatusBar.goToKeyguard();
+                    mShadeController.goToKeyguard();
                 }
                 return true;
             case StatusBarState.SHADE:
@@ -2521,8 +2544,8 @@
     @Override
     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
         super.setHeadsUpManager(headsUpManager);
-        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
-                this);
+        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
+                mNotificationStackScroller.getHeadsUpCallback(), this);
     }
 
     public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
@@ -2605,7 +2628,7 @@
         }
         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
             mShowIconsWhenExpanded = showIconsWhenExpanded;
-            mStatusBar.recomputeDisableFlags(false);
+            mCommandQueue.recomputeDisableFlags(false);
         }
     }
 
@@ -2769,11 +2792,14 @@
         mNotificationStackScroller.setAnimationsEnabled(!disabled);
     }
 
-    public void setDozing(boolean dozing, boolean animate,
-            PointF wakeUpTouchLocation) {
-        mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
+            boolean passiveInterrupted) {
         if (dozing == mDozing) return;
         mDozing = dozing;
+        mSemiAwake = !EXPAND_ON_WAKE_UP && !mDozing && passiveInterrupted;
+        if (!mSemiAwake) {
+            mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+        }
 
         if (mBarState == StatusBarState.KEYGUARD
                 || mBarState == StatusBarState.SHADE_LOCKED) {
@@ -2787,24 +2813,38 @@
             } else {
                 mDarkAnimator.cancel();
             }
+            if (mSemiAwake) {
+                setDarkAmount(0, 0);
+            }
         }
         mDarkAmountTarget = darkAmount;
-        if (animate) {
-            if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
-                mDarkInterpolator = dozing
-                        ? Interpolators.FAST_OUT_SLOW_IN
-                        : Interpolators.TOUCH_RESPONSE_REVERSE;
+        if (!mSemiAwake) {
+            if (animate) {
+                startDarkAnimation();
+            } else {
+                setDarkAmount(darkAmount, darkAmount);
             }
-            mNotificationStackScroller.notifyDarkAnimationStart(dozing);
-            mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
-            mDarkAnimator.setInterpolator(Interpolators.LINEAR);
-            mDarkAnimator.setDuration(mNotificationStackScroller.getDarkAnimationDuration(dozing));
-            mDarkAnimator.start();
-        } else {
-            setDarkAmount(darkAmount, darkAmount);
         }
     }
 
+    public boolean isSemiAwake() {
+        return mSemiAwake;
+    }
+
+    private void startDarkAnimation() {
+        if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
+            mDarkInterpolator = mDozing
+                    ? Interpolators.FAST_OUT_SLOW_IN
+                    : Interpolators.TOUCH_RESPONSE_REVERSE;
+        }
+        mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
+        mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozing ? 1 : 0);
+        mDarkAnimator.setInterpolator(Interpolators.LINEAR);
+        mDarkAnimator.setDuration(
+                mNotificationStackScroller.getDarkAnimationDuration(mDozing));
+        mDarkAnimator.start();
+    }
+
     private void setDarkAmount(float linearAmount, float amount) {
         mInterpolatedDarkAmount = amount;
         mLinearDarkAmount = linearAmount;
@@ -2875,7 +2915,7 @@
             if (hideIcons != mHideIconsDuringNotificationLaunch) {
                 mHideIconsDuringNotificationLaunch = hideIcons;
                 if (!hideIcons) {
-                    mStatusBar.recomputeDisableFlags(true /* animate */);
+                    mCommandQueue.recomputeDisableFlags(true /* animate */);
                 }
             }
         }
@@ -2943,6 +2983,7 @@
         mNotificationStackScroller.updateSpeedBumpIndex();
         mNotificationStackScroller.updateFooter();
         updateShowEmptyShadeView();
+        mNotificationStackScroller.updateIconAreaViews();
     }
 
     public void onUpdateRowStates() {
@@ -2989,4 +3030,26 @@
         mNotificationStackScroller.setScrimController(scrimController);
         updateShowEmptyShadeView();
     }
+
+    public void showTransientIndication(int id) {
+        mKeyguardBottomArea.showTransientIndication(id);
+    }
+
+    /**
+     * Whenever a user drags down on the empty area (pulling down the shade and clock) and lets go.
+     *
+     * @return {@code true} if dragging down should take the user to SHADE_LOCKED.
+     */
+    public boolean onDraggedDown() {
+        if (isSemiAwake()) {
+            mSemiAwake = false;
+            mNotificationStackScroller.setDark(false /* dark */, true /* animate */,
+                    null /* touchLocation */);
+            startDarkAnimation();
+            mStatusBar.updateScrimController();
+
+            return WAKE_UP_TO_SHADE;
+        }
+        return true;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 57c2439..f29b7ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -227,6 +227,16 @@
         mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
     }
 
+    private void addMovement(MotionEvent event) {
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        float deltaX = event.getRawX() - event.getX();
+        float deltaY = event.getRawY() - event.getY();
+        event.offsetLocation(deltaX, deltaY);
+        mVelocityTracker.addMovement(event);
+        event.offsetLocation(-deltaX, -deltaY);
+    }
+
     public void setTouchAndAnimationDisabled(boolean disabled) {
         mTouchDisabled = disabled;
         if (mTouchDisabled) {
@@ -307,7 +317,7 @@
                 mTouchAboveFalsingThreshold = false;
                 mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
                         && mHeadsUpManager.hasPinnedHeadsUp();
-                mVelocityTracker.addMovement(event);
+                addMovement(event);
                 if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
                         || mPeekAnimator != null) {
                     mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
@@ -341,7 +351,7 @@
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
-                mVelocityTracker.addMovement(event);
+                addMovement(event);
                 float h = y - mInitialTouchY;
 
                 // If the panel was collapsed when touching, we only need to check for the
@@ -386,7 +396,7 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mVelocityTracker.addMovement(event);
+                addMovement(event);
                 endMotionEvent(event, x, y, false /* forceCancel */);
                 break;
         }
@@ -573,7 +583,7 @@
                 mHasLayoutedSinceDown = false;
                 mUpdateFlingOnLayout = false;
                 mTouchAboveFalsingThreshold = false;
-                mVelocityTracker.addMovement(event);
+                addMovement(event);
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 final int upPointer = event.getPointerId(event.getActionIndex());
@@ -593,7 +603,7 @@
                 break;
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
-                mVelocityTracker.addMovement(event);
+                addMovement(event);
                 if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
                     float hAbs = Math.abs(h);
                     if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 59863ec..2129835 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -19,6 +19,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -42,6 +43,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
@@ -52,6 +54,7 @@
     private static final boolean DEBUG = StatusBar.DEBUG;
     private static final boolean DEBUG_GESTURES = false;
     private static final int NO_VALUE = Integer.MIN_VALUE;
+    private final CommandQueue mCommandQueue;
 
     StatusBar mBar;
 
@@ -82,6 +85,7 @@
         super(context, attrs);
 
         mBarTransitions = new PhoneStatusBarTransitions(this);
+        mCommandQueue = getComponent(context, CommandQueue.class);
     }
 
     public BarTransitions getBarTransitions() {
@@ -166,7 +170,7 @@
 
     @Override
     public boolean panelEnabled() {
-        return mBar.panelsEnabled();
+        return mCommandQueue.panelsEnabled();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
index 15e189c..855592f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java
@@ -16,29 +16,269 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.StyleRes;
+import android.app.StatusBarManager;
+import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
 import android.view.ContextThemeWrapper;
+import android.view.IRotationWatcher.Stub;
+import android.view.MotionEvent;
+import android.view.Surface;
 import android.view.View;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
-import com.android.systemui.util.Utils;
+import com.android.systemui.statusbar.policy.RotationLockController;
+
+import java.util.Optional;
+import java.util.function.Consumer;
 
 public class RotationContextButton extends ContextualButton {
+    public static final boolean DEBUG_ROTATION = false;
+
+    private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
+    private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000;
+
+    private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3;
 
     private @StyleRes int mStyleRes;
 
-    public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId,
-            @StyleRes int style) {
-        super(buttonResId, iconResId);
-        mStyleRes = style;
+    private int mLastRotationSuggestion;
+    private boolean mPendingRotationSuggestion;
+    private boolean mHoveringRotationSuggestion;
+    private RotationLockController mRotationLockController;
+    private TaskStackListenerImpl mTaskStackListener;
+    private Consumer<Integer> mRotWatcherListener;
+    private boolean mIsNavigationBarShowing;
+
+    private final Runnable mRemoveRotationProposal =
+            () -> setRotateSuggestionButtonState(false /* visible */);
+    private final Runnable mCancelPendingRotationProposal =
+            () -> mPendingRotationSuggestion = false;
+    private Animator mRotateHideAnimator;
+    private boolean mAccessibilityFeedbackEnabled;
+
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final ViewRippler mViewRippler = new ViewRippler();
+
+    private final Stub mRotationWatcher = new Stub() {
+        @Override
+        public void onRotationChanged(final int rotation) throws RemoteException {
+            // We need this to be scheduled as early as possible to beat the redrawing of
+            // window in response to the orientation change.
+            Handler h = getCurrentView().getHandler();
+            Message msg = Message.obtain(h, () -> {
+                // If the screen rotation changes while locked, potentially update lock to flow with
+                // new screen rotation and hide any showing suggestions.
+                if (mRotationLockController.isRotationLocked()) {
+                    if (shouldOverrideUserLockPrefs(rotation)) {
+                        setRotationLockedAtAngle(rotation);
+                    }
+                    setRotateSuggestionButtonState(false /* visible */, true /* forced */);
+                }
+
+                if (mRotWatcherListener != null) {
+                    mRotWatcherListener.accept(rotation);
+                }
+            });
+            msg.setAsynchronous(true);
+            h.sendMessageAtFrontOfQueue(msg);
+        }
+    };
+
+    /**
+     * Determines if rotation suggestions disabled2 flag exists in flag
+     * @param disable2Flags see if rotation suggestion flag exists in this flag
+     * @return whether flag exists
+     */
+    static boolean hasDisable2RotateSuggestionFlag(int disable2Flags) {
+        return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
     }
 
-    public void setStyle(@StyleRes int styleRes) {
-        mStyleRes = styleRes;
+    public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId,
+            @NonNull Context context, @StyleRes int style) {
+        super(buttonResId, iconResId);
+
+        mStyleRes = style;
+        mIsNavigationBarShowing = true;
+        mRotationLockController = Dependency.get(RotationLockController.class);
+
+        // Register the task stack listener
+        mTaskStackListener = new TaskStackListenerImpl();
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        setOnClickListener(this::onRotateSuggestionClick);
+        setOnHoverListener(this::onRotateSuggestionHover);
+
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                    .watchRotation(mRotationWatcher, context.getDisplay().getDisplayId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    public void addRotationCallback(Consumer<Integer> watcher) {
+        mRotWatcherListener = watcher;
+    }
+
+    public void setRotateSuggestionButtonState(boolean visible) {
+        setRotateSuggestionButtonState(visible, false /* force */);
+    }
+
+    public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
+        // At any point the the button can become invisible because an a11y service became active.
+        // Similarly, a call to make the button visible may be rejected because an a11y service is
+        // active. Must account for this.
+        // Rerun a show animation to indicate change but don't rerun a hide animation
+        if (!visible && !isVisible()) return;
+
+        final View view = getCurrentView();
+        if (view == null) return;
+
+        final KeyButtonDrawable currentDrawable = getImageDrawable();
+        if (currentDrawable == null) return;
+
+        // Clear any pending suggestion flag as it has either been nullified or is being shown
+        mPendingRotationSuggestion = false;
+        view.removeCallbacks(mCancelPendingRotationProposal);
+
+        // Handle the visibility change and animation
+        if (visible) { // Appear and change (cannot force)
+            // Stop and clear any currently running hide animations
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                mRotateHideAnimator.cancel();
+            }
+            mRotateHideAnimator = null;
+
+            // Reset the alpha if any has changed due to hide animation
+            view.setAlpha(1f);
+
+            // Run the rotate icon's animation if it has one
+            if (currentDrawable.canAnimate()) {
+                currentDrawable.resetAnimation();
+                currentDrawable.startAnimation();
+            }
+
+            if (!isRotateSuggestionIntroduced()) mViewRippler.start(view);
+
+            // Set visibility unless a11y service is active.
+            show();
+        } else { // Hide
+            mViewRippler.stop(); // Prevent any pending ripples, force hide or not
+
+            if (force) {
+                // If a hide animator is running stop it and make invisible
+                if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                    mRotateHideAnimator.pause();
+                }
+                hide();
+                return;
+            }
+
+            // Don't start any new hide animations if one is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+
+            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", 0f);
+            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
+            fadeOut.setInterpolator(Interpolators.LINEAR);
+            fadeOut.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    hide();
+                }
+            });
+
+            mRotateHideAnimator = fadeOut;
+            fadeOut.start();
+        }
+    }
+
+    public void setAccessibilityFeedbackEnabled(boolean flag) {
+        mAccessibilityFeedbackEnabled = flag;
+    }
+
+    public void setRotationLockedAtAngle(int rotationSuggestion) {
+        mRotationLockController.setRotationLockedAtAngle(true /* locked */, rotationSuggestion);
+    }
+
+    public boolean isRotationLocked() {
+        return mRotationLockController.isRotationLocked();
+    }
+
+    public void onRotationProposal(int rotation, int windowRotation, boolean isValid) {
+        // This method will be called on rotation suggestion changes even if the proposed rotation
+        // is not valid for the top app. Use invalid rotation choices as a signal to remove the
+        // rotate button if shown.
+        if (!isValid) {
+            setRotateSuggestionButtonState(false /* visible */);
+            return;
+        }
+
+        // If window rotation matches suggested rotation, remove any current suggestions
+        if (rotation == windowRotation) {
+            getCurrentView().removeCallbacks(mRemoveRotationProposal);
+            setRotateSuggestionButtonState(false /* visible */);
+            return;
+        }
+
+        // Prepare to show the navbar icon by updating the icon style to change anim params
+        mLastRotationSuggestion = rotation; // Remember rotation for click
+        final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
+        int style;
+        if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
+            style = rotationCCW ? R.style.RotateButtonCCWStart90 : R.style.RotateButtonCWStart90;
+        } else { // 90 or 270
+            style = rotationCCW ? R.style.RotateButtonCCWStart0 : R.style.RotateButtonCWStart0;
+        }
+        mStyleRes = style;
+        updateIcon();
+
+        if (mIsNavigationBarShowing) {
+            // The navbar is visible so show the icon right away
+            showAndLogRotationSuggestion();
+        } else {
+            // If the navbar isn't shown, flag the rotate icon to be shown should the navbar become
+            // visible given some time limit.
+            mPendingRotationSuggestion = true;
+            getCurrentView().removeCallbacks(mCancelPendingRotationProposal);
+            getCurrentView().postDelayed(mCancelPendingRotationProposal,
+                    NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS);
+        }
+    }
+
+    public void onDisable2FlagChanged(int state2) {
+        final boolean rotateSuggestionsDisabled = hasDisable2RotateSuggestionFlag(state2);
+        if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled();
+    }
+
+    public void onNavigationBarWindowVisibilityChange(boolean showing) {
+        if (mIsNavigationBarShowing != showing) {
+            mIsNavigationBarShowing = showing;
+
+            // If the navbar is visible, show the rotate button if there's a pending suggestion
+            if (showing && mPendingRotationSuggestion) {
+                showAndLogRotationSuggestion();
+            }
+        }
     }
 
     @Override
@@ -58,4 +298,168 @@
         Context context = new ContextThemeWrapper(getContext().getApplicationContext(), mStyleRes);
         return KeyButtonDrawable.create(context, mIconResId, false /* shadow */);
     }
+
+    @Override
+    public void onDestroy() {
+        // Unregister the task stack listener
+        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+
+        try {
+            WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mRotationWatcher);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private void onRotateSuggestionClick(View v) {
+        mMetricsLogger.action(MetricsEvent.ACTION_ROTATION_SUGGESTION_ACCEPTED);
+        incrementNumAcceptedRotationSuggestionsIfNeeded();
+        setRotationLockedAtAngle(mLastRotationSuggestion);
+    }
+
+    private boolean onRotateSuggestionHover(View v, MotionEvent event) {
+        final int action = event.getActionMasked();
+        mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
+                || (action == MotionEvent.ACTION_HOVER_MOVE);
+        rescheduleRotationTimeout(true /* reasonHover */);
+        return false; // Must return false so a11y hover events are dispatched correctly.
+    }
+
+    private void onRotationSuggestionsDisabled() {
+        // Immediately hide the rotate button and clear any planned removal
+        setRotateSuggestionButtonState(false /* visible */, true /* force */);
+        getCurrentView().removeCallbacks(mRemoveRotationProposal);
+    }
+
+    private void showAndLogRotationSuggestion() {
+        setRotateSuggestionButtonState(true /* visible */);
+        rescheduleRotationTimeout(false /* reasonHover */);
+        mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN);
+    }
+
+    private boolean shouldOverrideUserLockPrefs(final int rotation) {
+        // Only override user prefs when returning to the natural rotation (normally portrait).
+        // Don't let apps that force landscape or 180 alter user lock.
+        return rotation == NATURAL_ROTATION;
+    }
+
+    private boolean isRotationAnimationCCW(int from, int to) {
+        // All 180deg WM rotation animations are CCW, match that
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
+        return false; // Default
+    }
+
+    private void rescheduleRotationTimeout(final boolean reasonHover) {
+        // May be called due to a new rotation proposal or a change in hover state
+        if (reasonHover) {
+            // Don't reschedule if a hide animator is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+            // Don't reschedule if not visible
+            if (!isVisible()) return;
+        }
+
+        // Stop any pending removal
+        getCurrentView().removeCallbacks(mRemoveRotationProposal);
+        // Schedule timeout
+        getCurrentView().postDelayed(mRemoveRotationProposal, computeRotationProposalTimeout());
+    }
+
+    private int computeRotationProposalTimeout() {
+        if (mAccessibilityFeedbackEnabled) return 20000;
+        if (mHoveringRotationSuggestion) return 16000;
+        return 10000;
+    }
+
+    private boolean isRotateSuggestionIntroduced() {
+        ContentResolver cr = getContext().getContentResolver();
+        return Settings.Secure.getInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0)
+                >= NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION;
+    }
+
+    private void incrementNumAcceptedRotationSuggestionsIfNeeded() {
+        // Get the number of accepted suggestions
+        ContentResolver cr = getContext().getContentResolver();
+        final int numSuggestions = Settings.Secure.getInt(cr,
+                Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0);
+
+        // Increment the number of accepted suggestions only if it would change intro mode
+        if (numSuggestions < NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION) {
+            Settings.Secure.putInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
+                    numSuggestions + 1);
+        }
+    }
+
+    private class TaskStackListenerImpl extends SysUiTaskStackChangeListener {
+        // Invalidate any rotation suggestion on task change or activity orientation change
+        // Note: all callbacks happen on main thread
+
+        @Override
+        public void onTaskStackChanged() {
+            setRotateSuggestionButtonState(false /* visible */);
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            setRotateSuggestionButtonState(false /* visible */);
+        }
+
+        @Override
+        public void onTaskMovedToFront(int taskId) {
+            setRotateSuggestionButtonState(false /* visible */);
+        }
+
+        @Override
+        public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
+            // Only hide the icon if the top task changes its requestedOrientation
+            // Launcher can alter its requestedOrientation while it's not on top, don't hide on this
+            Optional.ofNullable(ActivityManagerWrapper.getInstance())
+                    .map(ActivityManagerWrapper::getRunningTask)
+                    .ifPresent(a -> {
+                        if (a.id == taskId) setRotateSuggestionButtonState(false /* visible */);
+                    });
+        }
+    }
+
+    private class ViewRippler {
+        private static final int RIPPLE_OFFSET_MS = 50;
+        private static final int RIPPLE_INTERVAL_MS = 2000;
+        private View mRoot;
+
+        public void start(View root) {
+            stop(); // Stop any pending ripple animations
+
+            mRoot = root;
+
+            // Schedule pending ripples, offset the 1st to avoid problems with visibility change
+            mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
+            mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
+            mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
+            mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
+            mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
+        }
+
+        public void stop() {
+            if (mRoot != null) mRoot.removeCallbacks(mRipple);
+        }
+
+        private final Runnable mRipple = new Runnable() {
+            @Override
+            public void run() { // Cause the ripple to fire via false presses
+                if (!mRoot.isAttachedToWindow()) return;
+                mRoot.setPressed(true /* pressed */);
+                mRoot.setPressed(false /* pressed */);
+            }
+        };
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e3a7b75..1bed26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -63,7 +63,7 @@
 public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
         Dumpable {
 
-    private static final String TAG = "ScrimController";
+    static final String TAG = "ScrimController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     /**
@@ -96,6 +96,11 @@
      */
     public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.70f;
     /**
+     * A scrim varies its opacity based on a busyness factor, for example
+     * how many notifications are currently visible.
+     */
+    public static final float GRADIENT_SCRIM_DARK_KEYGUARD = 0.80f;
+    /**
      * The most common scrim, the one under the keyguard.
      */
     protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
@@ -361,7 +366,7 @@
             mExpansionFraction = fraction;
 
             final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
-                    || mState == ScrimState.KEYGUARD;
+                    || mState == ScrimState.KEYGUARD || mState == ScrimState.DARK_KEYGUARD;
             if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 085f7b6..ade063d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -49,6 +49,8 @@
                     // fade it out afterwards.
                     mBlankScreen = true;
                 }
+            } else if (previousState == ScrimState.KEYGUARD) {
+                mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
             } else {
                 mAnimationDuration = ScrimController.ANIMATION_DURATION;
             }
@@ -59,8 +61,24 @@
         @Override
         public float getBehindAlpha(float busynessFactor) {
             return MathUtils.map(0 /* start */, 1 /* stop */,
-                   mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
-                   busynessFactor);
+                    mScrimBehindAlphaKeyguard, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+                    busynessFactor);
+        }
+    },
+
+    /**
+     * On semi-awake lock screen.
+     */
+    DARK_KEYGUARD(7) {
+
+        @Override
+        public void prepare(ScrimState previousState) {
+            mBlankScreen = mDisplayRequiresBlanking && previousState != ScrimState.AOD;
+            mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
+            mCurrentBehindAlpha = ScrimController.GRADIENT_SCRIM_DARK_KEYGUARD;
+            mCurrentInFrontAlpha = 0;
+            mCurrentInFrontTint = Color.BLACK;
+            mCurrentBehindTint = Color.BLACK;
         }
     },
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
new file mode 100644
index 0000000..e546119
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.view.View;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+
+/**
+ * {@link ShadeController} is an abstraction of the work that used to be hard-coded in
+ * {@link StatusBar}. The shade itself represents the concept of the status bar window state, and
+ * can be in multiple states: dozing, locked, showing the bouncer, occluded, etc. All/some of these
+ * are coordinated with {@link StatusBarKeyguardViewManager} via
+ * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others.
+ */
+public interface ShadeController {
+
+    /**
+     * Shows the keyguard bouncer - the password challenge on the lock screen
+     *
+     * @param scrimmed true when the bouncer should show scrimmed, false when the user will be
+     * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
+     */
+    void showBouncer(boolean scrimmed);
+
+    /**
+     * Make our window larger and the panel expanded
+     */
+    void instantExpandNotificationsPanel();
+
+    /**
+     * If the notifications panel is not fully expanded, collapse it animated.
+     *
+     * @return Seems to always return false
+     */
+    boolean closeShadeIfOpen();
+
+    /**
+     * Add a runnable for NotificationPanelView to post when the panel is expanded.
+     *
+     * @param action the action to post
+     */
+    void postOnShadeExpanded(Runnable action);
+
+    /**
+     * Add a runnable to be executed after the shade collapses. Post-collapse runnables are
+     * aggregated and run serially.
+     *
+     * @param action the action to execute
+     */
+    void addPostCollapseAction(Runnable action);
+
+    /**
+     * Ask shade controller to set the state to {@link StatusBarState#KEYGUARD}, but only from
+     * {@link StatusBarState#SHADE_LOCKED}
+     */
+    void goToKeyguard();
+
+    /**
+     * When the keyguard is showing and covered by something (bouncer, keyguard activity, etc.) it
+     * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
+     *
+     * @return whether the keyguard is currently occluded
+     */
+    boolean isOccluded();
+
+    /**
+     * Notify the shade controller that the current user changed
+     *
+     * @param newUserId userId of the new user
+     */
+    void setLockscreenUser(int newUserId);
+
+    /**
+     * Dozing is when the screen is in AOD or asleep
+     *
+     * @return true if we are dozing
+     */
+    boolean isDozing();
+
+    /**
+     * Ask the display to wake up if currently dozing, else do nothing
+     *
+     * @param time when to wake up
+     * @param view the view requesting the wakeup
+     */
+    void wakeUpIfDozing(long time, View view);
+
+    /**
+     * If secure with redaction: Show bouncer, go to unlocked shade.
+     *
+     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
+     *
+     * @param startingChild The view to expand after going to the shade.
+     */
+    void goToLockedShade(View startingChild);
+
+    /**
+     * Adds a {@param runnable} to be executed after Keyguard is gone.
+     */
+    void addAfterKeyguardGoneRunnable(Runnable runnable);
+
+    /**
+     * Close the shade if it was open
+     *
+     * @return true if the shade was open, else false
+     */
+    boolean collapsePanel();
+
+    /**
+     * If {@param animate}, does the same as {@link #collapsePanel()}. Otherwise, instantly collapse
+     * the panel. Post collapse runnables will be executed
+     *
+     * @param animate
+     */
+    void collapsePanel(boolean animate);
+
+    /**
+     * Callback to tell the shade controller that an activity launch animation was canceled
+     */
+    void onLaunchAnimationCancelled();
+
+    /**
+     * When notifications update, give the shade controller a chance to do thing in response to
+     * the new data set
+     */
+    void updateAreThereNotifications();
+
+    /**
+     * Callback to notify the shade controller that a {@link ActivatableNotificationView} has become
+     * inactive
+     */
+    void onActivationReset();
+
+}
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 56bef2e..b9ca949 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -26,10 +26,8 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
 import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
-import static com.android.systemui.statusbar.NotificationLockscreenUserManager
-        .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
+import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -54,7 +52,6 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
-import android.app.TaskStackBuilder;
 import android.app.UiModeManager;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
@@ -65,31 +62,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.IntentSender;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.media.AudioAttributes;
-import android.media.MediaMetadata;
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -105,14 +92,10 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.StatusBarNotification;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
-import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
@@ -122,7 +105,6 @@
 import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -130,19 +112,15 @@
 import android.view.animation.AccelerateInterpolator;
 import android.widget.DateTimeView;
 import android.widget.ImageView;
-import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.MessagingGroup;
 import com.android.internal.widget.MessagingMessage;
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
@@ -152,11 +130,11 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.InitController;
 import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
-import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
@@ -185,38 +163,34 @@
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.WindowManagerProxy;
-import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
-import com.android.systemui.statusbar.notification.AppOpsListener;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInfo;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.AppOpsListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
@@ -230,7 +204,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -253,9 +226,9 @@
 public class StatusBar extends SystemUI implements DemoMode,
         ActivityStarter, OnUnlockMethodChangedListener,
         OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
-        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter,
-        StatusBarStateController.StateListener,  AmbientPulseManager.OnAmbientChangedListener,
-        ActivityLaunchAnimator.Callback {
+        ColorExtractor.OnColorsChangedListener, ConfigurationListener,
+        StatusBarStateController.StateListener, ShadeController,
+        ActivityLaunchAnimator.Callback, AmbientPulseManager.OnAmbientChangedListener {
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_CHILD_NOTIFICATIONS
@@ -324,10 +297,10 @@
 
     /** If true, the system is in the half-boot-to-decryption-screen state.
      * Prudently disable QS and notifications.  */
-    private static final boolean ONLY_CORE_APPS;
+    public static final boolean ONLY_CORE_APPS;
 
     /** If true, the lockscreen will show a distinct wallpaper */
-    private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
+    public static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
 
     static {
         boolean onlyCoreApps;
@@ -376,7 +349,6 @@
 
     // expanded notifications
     protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
-    private TextView mNotificationPanelDebugText;
 
     // settings
     private QSPanel mQSPanel;
@@ -385,15 +357,12 @@
 
     // RemoteInputView to be activated after unlock
     private View mPendingRemoteInputView;
-    private View mPendingWorkRemoteInputView;
 
     private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler =
             Dependency.get(RemoteInputQuickSettingsDisabler.class);
 
     private View mReportRejectedTouch;
 
-    private int mMaxAllowedKeyguardNotifications;
-
     private boolean mExpandedVisible;
 
     private final int[] mAbsPos = new int[2];
@@ -460,7 +429,6 @@
     private int mInteractingWindows;
     private boolean mAutohideSuspended;
     private int mStatusBarMode;
-    private int mMaxKeyguardNotifications;
 
     private ViewMediatorCallback mKeyguardViewMediatorCallback;
     protected ScrimController mScrimController;
@@ -479,9 +447,6 @@
 
     protected BackDropView mBackdrop;
     protected ImageView mBackdropFront, mBackdropBack;
-    protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
-    protected final PorterDuffXfermode mSrcOverXferMode =
-            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
 
     private NotificationMediaManager mMediaManager;
     protected NotificationLockscreenUserManager mLockscreenUserManager;
@@ -524,7 +489,6 @@
     private boolean mIsOccluded;
     private boolean mWereIconsJustHidden;
     private boolean mBouncerWasShowingWhenHidden;
-    private boolean mIsCollapsingToShowActivityOverLockscreen;
 
     // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
     // this animation is tied to the scrim for historic reasons.
@@ -557,13 +521,9 @@
     private BatteryController mBatteryController;
     protected boolean mPanelExpanded;
     private UiModeManager mUiModeManager;
-    private boolean mKeyguardRequested;
     private boolean mIsKeyguard;
     private LogMaker mStatusBarStateLog;
-    private final LockscreenGestureLogger mLockscreenGestureLogger =
-            Dependency.get(LockscreenGestureLogger.class);
     protected NotificationIconAreaController mNotificationIconAreaController;
-    private boolean mReinflateNotificationsOnUserSwitched;
     @Nullable private View mAmbientIndicationContainer;
     private SysuiColorExtractor mColorExtractor;
     private ScreenLifecycle mScreenLifecycle;
@@ -598,10 +558,10 @@
 
     private NavigationBarFragment mNavigationBar;
     private View mNavigationBarView;
-    protected ActivityLaunchAnimator mActivityLaunchAnimator;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private boolean mVibrateOnOpening;
     private VibratorHelper mVibratorHelper;
+    protected NotificationPresenter mPresenter;
 
     @Override
     public void start() {
@@ -626,11 +586,11 @@
         mEntryManager = Dependency.get(NotificationEntryManager.class);
         mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
         mAppOpsListener = Dependency.get(AppOpsListener.class);
-        mAppOpsListener.setUpWithPresenter(this, mEntryManager);
         mZenController = Dependency.get(ZenModeController.class);
         mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
-
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
+        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+
         mColorExtractor.addOnColorsChangedListener(this);
         mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
 
@@ -659,17 +619,12 @@
 
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
 
-        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
         mRecents = getComponent(Recents.class);
 
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mLockPatternUtils = new LockPatternUtils(mContext);
-
-        mMediaManager.setUpWithPresenter(this, mEntryManager);
 
         // Connect in to the status bar manager service
         mCommandQueue = getComponent(CommandQueue.class);
@@ -696,8 +651,9 @@
                 wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */);
         mWallpaperChangedReceiver.onReceive(mContext, null);
 
-        mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
-        mCommandQueue.disable(switches[0], switches[6], false /* animate */);
+        // Set up the initial notification state. This needs to happen before CommandQueue.disable()
+        setUpPresenter();
+
         setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
                 fullscreenStackBounds, dockedStackBounds);
         topAppWindowChanged(switches[2] != 0);
@@ -710,8 +666,6 @@
             mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
         }
 
-        // Set up the initial notification state.
-        mNotificationListener.setUpWithPresenter(this, mEntryManager);
 
         if (DEBUG) {
             Log.d(TAG, String.format(
@@ -724,24 +678,12 @@
                    ));
         }
 
-        setHeadsUpUser(mLockscreenUserManager.getCurrentUserId());
-
         IntentFilter internalFilter = new IntentFilter();
         internalFilter.addAction(BANNER_ACTION_CANCEL);
         internalFilter.addAction(BANNER_ACTION_SETUP);
         mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
                 null);
 
-        IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
-                Context.VR_SERVICE));
-        if (vrManager != null) {
-            try {
-                vrManager.registerListener(mVrStateCallbacks);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
-            }
-        }
-
         IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
                 ServiceManager.getService(Context.WALLPAPER_SERVICE));
         try {
@@ -769,6 +711,13 @@
         Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
 
         Dependency.get(ConfigurationController.class).addCallback(this);
+
+        // set the initial view visibility
+        Dependency.get(InitController.class).addPostInitTask(this::updateAreThereNotifications);
+        Dependency.get(InitController.class).addPostInitTask(() -> {
+            setUpDisableFlags(switches[0], switches[6]);
+        });
+
     }
 
     // ================================================================================
@@ -788,24 +737,9 @@
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
-        NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
         mZenController.addCallback(this);
-        mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow,
-                this,
-                mNotificationPanel,
-                notifListContainer);
-        mGutsManager.setUpWithPresenter(this, notifListContainer, mCheckSaveListener,
-                key -> {
-                    try {
-                        mBarService.onNotificationSettingsViewed(key);
-                    } catch (RemoteException e) {
-                        // if we're here we're dead
-                    }
-                });
-        mNotificationLogger.setUpWithEntryManager(mEntryManager, notifListContainer);
-        mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
-        mAboveShelfObserver.setListener(mStatusBarWindow.findViewById(
-                R.id.notification_container_parent));
+        NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
+        mNotificationLogger.setUpWithContainer(notifListContainer);
 
         mNotificationIconAreaController = SystemUIFactory.getInstance()
                 .createNotificationIconAreaController(context, this);
@@ -850,7 +784,7 @@
                             mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
                     mHeadsUpAppearanceController.readFrom(oldController);
                     mStatusBarWindow.setStatusBarView(mStatusBarView);
-                    setAreThereNotifications();
+                    updateAreThereNotifications();
                     checkBarModes();
                 }).getFragmentManager()
                 .beginTransaction()
@@ -872,13 +806,6 @@
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
 
-        mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
-        mViewHierarchyManager.setUpWithPresenter(this, mEntryManager, notifListContainer);
-
-        if (MULTIUSER_DEBUG) {
-            mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
-            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
-        }
 
         try {
             boolean showNav = mWindowManagerService.hasNavigationBar();
@@ -890,10 +817,6 @@
             // no window manager? good luck with that
         }
 
-        mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
-        mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
-        mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
-
         if (ENABLE_LOCKSCREEN_WALLPAPER) {
             mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
         }
@@ -908,9 +831,6 @@
         mAmbientIndicationContainer = mStatusBarWindow.findViewById(
                 R.id.ambient_indication_container);
 
-        // set the initial view visibility
-        setAreThereNotifications();
-
         // TODO: Find better place for this callback.
         mBatteryController.addCallback(new BatteryStateChangeCallback() {
             @Override
@@ -948,6 +868,12 @@
         mDozeScrimController = new DozeScrimController(mScrimController, context,
                 DozeParameters.getInstance(context));
 
+        mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+        mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
+        mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
+        mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack,
+                mScrimController, mLockscreenWallpaper);
+
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
 
@@ -1053,6 +979,50 @@
         ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
     }
 
+    protected void setUpPresenter() {
+        // Set up the initial notification state.
+        mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
+                mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
+                mScrimController, this);
+        mAppOpsListener.setUpWithPresenter(mPresenter);
+        mNotificationListener.setUpWithPresenter(mPresenter);
+        mNotificationShelf.setOnActivatedListener(mPresenter);
+        mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
+    }
+
+    /**
+     * Post-init task of {@link #start()}
+     * @param state1 disable1 flags
+     * @param state2 disable2 flags
+     */
+    protected void setUpDisableFlags(int state1, int state2) {
+        mCommandQueue.disable(state1, state2, false /* animate */);
+    }
+
+    @Override
+    public void addAfterKeyguardGoneRunnable(Runnable runnable) {
+        mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+    }
+
+    @Override
+    public boolean isDozing() {
+        return mDozing && mNotificationPanel.isFullyDark();
+    }
+
+    @Override
+    public void wakeUpIfDozing(long time, View where) {
+        if (mDozing) {
+            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            pm.wakeUp(time, "com.android.systemui:NODOZE");
+            mWakeUpComingFromTouch = true;
+            where.getLocationInWindow(mTmpInt2);
+            mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
+                    mTmpInt2[1] + where.getHeight() / 2);
+            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+            mFalsingManager.onScreenOnFromTouch();
+        }
+    }
+
     protected void createNavigationBar() {
         mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
             mNavigationBar = (NavigationBarFragment) fragment;
@@ -1084,7 +1054,6 @@
         mNotificationShelf =
                 (NotificationShelf) LayoutInflater.from(mContext).inflate(
                         R.layout.status_bar_notification_shelf, mStackScroller, false);
-        mNotificationShelf.setOnActivatedListener(this);
         mNotificationShelf.setOnClickListener(mGoToLockedShadeListener);
     }
 
@@ -1092,13 +1061,6 @@
     public void onDensityOrFontScaleChanged() {
         MessagingMessage.dropCache();
         MessagingGroup.dropCache();
-        // start old BaseStatusBar.onDensityOrFontScaleChanged().
-        if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
-            mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
-        } else {
-            mReinflateNotificationsOnUserSwitched = true;
-        }
-        // end old BaseStatusBar.onDensityOrFontScaleChanged().
         // TODO: Remove this.
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.onDensityOrFontScaleChanged();
@@ -1169,8 +1131,6 @@
                 mScrimController, this, UnlockMethodCache.getInstance(mContext));
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
                 getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
-        //TODO: Can we put the keyguard view manager in Dependency?
-        mLockscreenUserManager.setKeyguardViewManager(mStatusBarKeyguardViewManager);
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
@@ -1178,6 +1138,7 @@
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
         mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
+        mMediaManager.setBiometricUnlockController(mBiometricUnlockController);
         Dependency.get(KeyguardDismissUtil.class).setDismissHandler(this::executeWhenUnlocked);
         Trace.endSection();
     }
@@ -1234,73 +1195,13 @@
         return true;
     }
 
-    @Override
-    public void onPerformRemoveNotification(StatusBarNotification n) {
-        if (mNotificationPanel.hasPulsingNotifications() &&
-                    !mAmbientPulseManager.hasNotifications()) {
-            // We were showing a pulse for a notification, but no notifications are pulsing anymore.
-            // Finish the pulse.
-            mDozeScrimController.pulseOutNow();
-        }
-    }
-
-    @Override
-    public void updateNotificationViews() {
-        // The function updateRowStates depends on both of these being non-null, so check them here.
-        // We may be called before they are set from DeviceProvisionedController's callback.
-        if (mScrimController == null) return;
-
-        // Do not modify the notifications during collapse.
-        if (isCollapsing()) {
-            addPostCollapseAction(this::updateNotificationViews);
-            return;
-        }
-
-        mViewHierarchyManager.updateNotificationViews();
-
-        mNotificationPanel.updateNotificationViews();
-
-        updateQsExpansionEnabled();
-
-        // Let's also update the icons
-        mNotificationIconAreaController.updateNotificationIcons();
-    }
-
-    @Override
-    public void onNotificationAdded(Entry shadeEntry) {
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-    }
-
-    @Override
-    public void onNotificationUpdated(StatusBarNotification notification) {
-        setAreThereNotifications();
-    }
-
-    @Override
-    public void onNotificationRemoved(String key, StatusBarNotification old) {
-        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
-
-        if (old != null) {
-            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
-                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
-                if (mState == StatusBarState.SHADE) {
-                    animateCollapsePanels();
-                } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
-                    goToKeyguard();
-                }
-            }
-        }
-        setAreThereNotifications();
-    }
-
     /**
      * Disable QS if device not provisioned.
      * If the user switcher is simple then disable QS during setup because
      * the user intends to use the lock screen user switcher, QS in not needed.
      */
     private void updateQsExpansionEnabled() {
-        mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
+        mNotificationPanel.setQsExpansionEnabled(mDeviceProvisionedController.isDeviceProvisioned()
                 && (mUserSetup || mUserSwitcherController == null
                         || !mUserSwitcherController.isSimpleUserSwitcher())
                 && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0)
@@ -1337,12 +1238,11 @@
         mEntryManager.updateNotifications();
     }
 
-    protected void setAreThereNotifications() {
-
+    public void updateAreThereNotifications() {
         if (SPEW) {
             final boolean clearable = hasActiveNotifications() &&
                     mNotificationPanel.hasActiveClearableNotifications();
-            Log.d(TAG, "setAreThereNotifications: N=" +
+            Log.d(TAG, "updateAreThereNotifications: N=" +
                     mEntryManager.getNotificationData().getActiveNotifications().size() + " any=" +
                     hasActiveNotifications() + " clearable=" + clearable);
         }
@@ -1368,192 +1268,9 @@
                         .start();
             }
         }
-
         mMediaManager.findAndUpdateMediaNotifications();
     }
 
-
-    /**
-     * Hide the album artwork that is fading out and release its bitmap.
-     */
-    protected final Runnable mHideBackdropFront = new Runnable() {
-        @Override
-        public void run() {
-            if (DEBUG_MEDIA) {
-                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
-            }
-            mBackdropFront.setVisibility(View.INVISIBLE);
-            mBackdropFront.animate().cancel();
-            mBackdropFront.setImageDrawable(null);
-        }
-    };
-
-    // TODO: Move this to NotificationMediaManager.
-    /**
-     * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
-     */
-    @Override
-    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
-        Trace.beginSection("StatusBar#updateMediaMetaData");
-        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
-            Trace.endSection();
-            return;
-        }
-
-        if (mBackdrop == null) {
-            Trace.endSection();
-            return; // called too early
-        }
-
-        boolean wakeAndUnlock = mBiometricUnlockController != null
-            && mBiometricUnlockController.isWakeAndUnlock();
-        if (mLaunchTransitionFadingAway || wakeAndUnlock) {
-            mBackdrop.setVisibility(View.INVISIBLE);
-            Trace.endSection();
-            return;
-        }
-
-        MediaMetadata mediaMetadata = mMediaManager.getMediaMetadata();
-
-        if (DEBUG_MEDIA) {
-            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
-                    + mMediaManager.getMediaNotificationKey()
-                    + " metadata=" + mediaMetadata
-                    + " metaDataChanged=" + metaDataChanged
-                    + " state=" + mState);
-        }
-
-        Drawable artworkDrawable = null;
-        if (mediaMetadata != null) {
-            Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
-            if (artworkBitmap == null) {
-                artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
-                // might still be null
-            }
-            if (artworkBitmap != null) {
-                artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), artworkBitmap);
-            }
-        }
-        boolean allowWhenShade = false;
-        if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
-            Bitmap lockWallpaper = mLockscreenWallpaper.getBitmap();
-            if (lockWallpaper != null) {
-                artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
-                        mBackdropBack.getResources(), lockWallpaper);
-                // We're in the SHADE mode on the SIM screen - yet we still need to show
-                // the lockscreen wallpaper in that mode.
-                allowWhenShade = mStatusBarKeyguardViewManager != null
-                        && mStatusBarKeyguardViewManager.isShowing();
-            }
-        }
-
-        boolean hideBecauseOccluded = mStatusBarKeyguardViewManager != null
-                && mStatusBarKeyguardViewManager.isOccluded();
-
-        final boolean hasArtwork = artworkDrawable != null;
-        mColorExtractor.setHasBackdrop(hasArtwork);
-        if (mScrimController != null) {
-            mScrimController.setHasBackdrop(hasArtwork);
-        }
-
-        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
-                && (mState != StatusBarState.SHADE || allowWhenShade)
-                && mBiometricUnlockController.getMode()
-                        != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                && !hideBecauseOccluded) {
-            // time to show some art!
-            if (mBackdrop.getVisibility() != View.VISIBLE) {
-                mBackdrop.setVisibility(View.VISIBLE);
-                if (allowEnterAnimation) {
-                    mBackdrop.setAlpha(0);
-                    mBackdrop.animate().alpha(1f);
-                } else {
-                    mBackdrop.animate().cancel();
-                    mBackdrop.setAlpha(1f);
-                }
-                mStatusBarWindowController.setBackdropShowing(true);
-                metaDataChanged = true;
-                if (DEBUG_MEDIA) {
-                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
-                }
-            }
-            if (metaDataChanged) {
-                if (mBackdropBack.getDrawable() != null) {
-                    Drawable drawable =
-                            mBackdropBack.getDrawable().getConstantState()
-                                    .newDrawable(mBackdropFront.getResources()).mutate();
-                    mBackdropFront.setImageDrawable(drawable);
-                    mBackdropFront.setAlpha(1f);
-                    mBackdropFront.setVisibility(View.VISIBLE);
-                } else {
-                    mBackdropFront.setVisibility(View.INVISIBLE);
-                }
-
-                if (DEBUG_MEDIA_FAKE_ARTWORK) {
-                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
-                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
-                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
-                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
-                } else {
-                    mBackdropBack.setImageDrawable(artworkDrawable);
-                }
-
-                if (mBackdropFront.getVisibility() == View.VISIBLE) {
-                    if (DEBUG_MEDIA) {
-                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
-                                + mBackdropFront.getDrawable()
-                                + " to "
-                                + mBackdropBack.getDrawable());
-                    }
-                    mBackdropFront.animate()
-                            .setDuration(250)
-                            .alpha(0f).withEndAction(mHideBackdropFront);
-                }
-            }
-        } else {
-            // need to hide the album art, either because we are unlocked, on AOD
-            // or because the metadata isn't there to support it
-            if (mBackdrop.getVisibility() != View.GONE) {
-                if (DEBUG_MEDIA) {
-                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
-                }
-                boolean cannotAnimateDoze = mDozing && !ScrimState.AOD.getAnimateChange();
-                if (mBiometricUnlockController.getMode()
-                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
-                        || hideBecauseOccluded || cannotAnimateDoze) {
-
-                    // We are unlocking directly - no animation!
-                    mBackdrop.setVisibility(View.GONE);
-                    mBackdropBack.setImageDrawable(null);
-                    mStatusBarWindowController.setBackdropShowing(false);
-                } else {
-                    mStatusBarWindowController.setBackdropShowing(false);
-                    mBackdrop.animate()
-                            .alpha(0)
-                            .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
-                            .setDuration(300)
-                            .setStartDelay(0)
-                            .withEndAction(() -> {
-                                mBackdrop.setVisibility(View.GONE);
-                                mBackdropFront.animate().cancel();
-                                mBackdropBack.setImageDrawable(null);
-                                mHandler.post(mHideBackdropFront);
-                            });
-                    if (mKeyguardMonitor.isKeyguardFadingAway()) {
-                        mBackdrop.animate()
-                                // Make it disappear faster, as the focus should be on the activity
-                                // behind.
-                                .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
-                                .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
-                                .setInterpolator(Interpolators.LINEAR)
-                                .start();
-                    }
-                }
-            }
-        }
-        Trace.endSection();
-    }
-
     private void updateReportRejectedTouchVisibility() {
         if (mReportRejectedTouch == null) {
             return;
@@ -1646,15 +1363,6 @@
         }
     }
 
-    /**
-     * Reapplies the disable flags as last requested by StatusBarManager.
-     *
-     * This needs to be called if state used by adjustDisableFlags changes.
-     */
-    public void recomputeDisableFlags(boolean animate) {
-        mCommandQueue.recomputeDisableFlags(animate);
-    }
-
     protected H createHandler() {
         return new StatusBar.H();
     }
@@ -1695,53 +1403,6 @@
         return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
     }
 
-    @Override
-    public boolean isDozing() {
-        return mDozing && mNotificationPanel.isFullyDark();
-    }
-
-    @Override
-    public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
-        if (isDozing()) {
-            return false;
-        }
-
-        if (mIsOccluded) {
-            boolean devicePublic = mLockscreenUserManager.
-                    isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
-            boolean userPublic = devicePublic
-                    || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
-            boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
-            if (userPublic && needsRedaction) {
-                return false;
-            }
-        }
-
-        if (!panelsEnabled()) {
-            if (DEBUG) {
-                Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
-            }
-            return false;
-        }
-
-        if (sbn.getNotification().fullScreenIntent != null) {
-            if (mAccessibilityManager.isTouchExplorationEnabled()) {
-                if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
-                return false;
-            } else {
-                // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
-                return !mStatusBarKeyguardViewManager.isShowing()
-                        || mStatusBarKeyguardViewManager.isOccluded();
-            }
-        }
-        return true;
-    }
-
-    @Override  // NotificationData.Environment
-    public String getCurrentMediaNotificationKey() {
-        return mMediaManager.getMediaNotificationKey();
-    }
-
     /**
      * To be called when there's a state change in StatusBarKeyguardViewManager.
      */
@@ -1815,12 +1476,6 @@
         }
     }
 
-    protected void setHeadsUpUser(int newUserId) {
-        if (mHeadsUpManager != null) {
-            mHeadsUpManager.setUser(newUserId);
-        }
-    }
-
     public boolean isKeyguardCurrentlySecure() {
         return !mUnlockMethodCache.canSkipBouncer();
     }
@@ -1868,6 +1523,11 @@
         return mAmbientIndicationContainer;
     }
 
+    @Override
+    public boolean isOccluded() {
+        return mIsOccluded;
+    }
+
     public void setOccluded(boolean occluded) {
         mIsOccluded = occluded;
         mScrimController.setKeyguardOccluded(occluded);
@@ -1901,10 +1561,10 @@
                 mWereIconsJustHidden = true;
                 mHandler.postDelayed(() -> {
                     mWereIconsJustHidden = false;
-                    recomputeDisableFlags(true);
+                    mCommandQueue.recomputeDisableFlags(true);
                 }, 500);
             } else {
-                recomputeDisableFlags(animate);
+                mCommandQueue.recomputeDisableFlags(animate);
             }
         }
         if (shouldHideIconsForBouncer) {
@@ -1912,20 +1572,21 @@
         }
     }
 
-    @Override
-    public void onLaunchAnimationCancelled() {
-        if (!isCollapsing()) {
-            onClosingFinished();
-        }
-    }
-
     public boolean isHeadsUpShouldBeVisible() {
         return mHeadsUpAppearanceController.shouldBeVisible();
     }
 
+    //TODO: These can / should probably be moved to NotificationPresenter or ShadeController
+    @Override
+    public void onLaunchAnimationCancelled() {
+        if (!mPresenter.isCollapsing()) {
+            onClosingFinished();
+        }
+    }
+
     @Override
     public void onExpandAnimationFinished(boolean launchIsFullScreen) {
-        if (!isCollapsing()) {
+        if (!mPresenter.isCollapsing()) {
             onClosingFinished();
         }
         if (launchIsFullScreen) {
@@ -1935,8 +1596,9 @@
 
     @Override
     public void onExpandAnimationTimedOut() {
-        if (isPresenterFullyCollapsed() && !isCollapsing()
-                && !mActivityLaunchAnimator.isLaunchForActivity()) {
+        ActivityLaunchAnimator animator = mPresenter.getActivityLaunchAnimator();
+        if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
+                && animator != null && !animator.isLaunchForActivity()) {
             onClosingFinished();
         } else {
             collapsePanel(true /* animate */);
@@ -1948,6 +1610,14 @@
         return mState == StatusBarState.SHADE;
     }
 
+    public boolean isDeviceInVrMode() {
+        return mPresenter.isDeviceInVrMode();
+    }
+
+    public NotificationPresenter getPresenter() {
+        return mPresenter;
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -2005,7 +1675,7 @@
     @Override
     public void handleSystemKey(int key) {
         if (SPEW) Log.d(TAG, "handleNavigationKey: " + key);
-        if (!panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive()
+        if (!mCommandQueue.panelsEnabled() || !mKeyguardMonitor.isDeviceInteractive()
                 || mKeyguardMonitor.isShowing() && !mKeyguardMonitor.isOccluded()) {
             return;
         }
@@ -2047,15 +1717,9 @@
         }
     }
 
-    boolean panelsEnabled() {
-        return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
-                && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
-                && !ONLY_CORE_APPS;
-    }
-
     void makeExpandedVisible(boolean force) {
         if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
-        if (!force && (mExpandedVisible || !panelsEnabled())) {
+        if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
             return;
         }
 
@@ -2066,7 +1730,7 @@
         mStatusBarWindowController.setPanelVisible(true);
 
         visibilityChanged(true);
-        recomputeDisableFlags(!force /* animate */);
+        mCommandQueue.recomputeDisableFlags(!force /* animate */);
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
     }
 
@@ -2099,12 +1763,12 @@
         }
     }
 
-    @Override
     public void animateCollapsePanels(int flags) {
         animateCollapsePanels(flags, false /* force */, false /* delayed */,
                 1.0f /* speedUpFactor */);
     }
 
+    @Override
     public void animateCollapsePanels(int flags, boolean force) {
         animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */);
     }
@@ -2155,7 +1819,7 @@
     }
 
     public void dispatchNotificationsPanelTouchEvent(MotionEvent ev) {
-        if (!panelsEnabled()) {
+        if (!mCommandQueue.panelsEnabled()) {
             return;
         }
         mNotificationPanel.dispatchTouchEvent(ev);
@@ -2173,7 +1837,7 @@
     @Override
     public void animateExpandNotificationsPanel() {
         if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
-        if (!panelsEnabled()) {
+        if (!mCommandQueue.panelsEnabled()) {
             return ;
         }
 
@@ -2185,7 +1849,7 @@
     @Override
     public void animateExpandSettingsPanel(@Nullable String subPanel) {
         if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
-        if (!panelsEnabled()) {
+        if (!mCommandQueue.panelsEnabled()) {
             return;
         }
 
@@ -2233,12 +1897,13 @@
 
         runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-        if (!mIsCollapsingToShowActivityOverLockscreen) {
+        if (!mPresenter.isCollapsingToShowActivityOverLockscreen()) {
             showBouncerIfKeyguard();
         } else if (DEBUG) {
             Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
         }
-        recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
+        mCommandQueue.recomputeDisableFlags(
+                mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
@@ -2330,7 +1995,7 @@
 
             // update low profile
             if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
-                setAreThereNotifications();
+                updateAreThereNotifications();
             }
 
             // ready to unhide
@@ -2703,9 +2368,6 @@
     private void addStatusBarWindow() {
         makeStatusBarView();
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
-        mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,
-                mNotificationPanel.createRemoteInputDelegate());
-        mRemoteInputManager.getController().addCallback(mStatusBarWindowController);
         mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
     }
 
@@ -2750,7 +2412,7 @@
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
             final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
             final Callback callback, int flags) {
-        if (onlyProvisioned && !isDeviceProvisioned()) return;
+        if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
 
         final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
                 mContext, intent, mLockscreenUserManager.getCurrentUserId());
@@ -2888,7 +2550,7 @@
                 }
             } else if (ACTION_FAKE_ARTWORK.equals(action)) {
                 if (DEBUG_MEDIA_FAKE_ARTWORK) {
-                    updateMediaMetaData(true, true);
+                    mPresenter.updateMediaMetaData(true, true);
                 }
             }
         }
@@ -2917,7 +2579,8 @@
         dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone);
     }
 
-    private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
+    @Override
+    public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
             boolean afterKeyguardGone) {
         if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
                 && mUnlockMethodCache.canSkipBouncer()
@@ -2951,38 +2614,10 @@
     }
 
     @Override
-    public void onUserSwitched(int newUserId) {
-        // Begin old BaseStatusBar.userSwitched
-        setHeadsUpUser(newUserId);
-        // End old BaseStatusBar.userSwitched
-        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
-        animateCollapsePanels();
-        if (mReinflateNotificationsOnUserSwitched) {
-            mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
-            mReinflateNotificationsOnUserSwitched = false;
-        }
-        updateNotificationViews();
-        mMediaManager.clearCurrentMediaNotification();
-        setLockscreenUser(newUserId);
-        mWallpaperChangedReceiver.onReceive(mContext, null);
-    }
-
-    @Override
-    public NotificationLockscreenUserManager getNotificationLockscreenUserManager() {
-        return mLockscreenUserManager;
-    }
-
-    @Override
-    public void onBindRow(Entry entry, PackageManager pmUser,
-            StatusBarNotification sbn, ExpandableNotificationRow row) {
-        row.setAboveShelfChangedListener(mAboveShelfObserver);
-        row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
-    }
-
-    protected void setLockscreenUser(int newUserId) {
+    public void setLockscreenUser(int newUserId) {
         mLockscreenWallpaper.setCurrentUser(newUserId);
         mScrimController.setCurrentUser(newUserId);
-        updateMediaMetaData(true, false);
+        mWallpaperChangedReceiver.onReceive(mContext, null);
     }
 
     /**
@@ -3020,8 +2655,6 @@
         if (mStatusBarWindowController != null && mNaturalBarHeight != oldBarHeight) {
             mStatusBarWindowController.setBarHeight(mNaturalBarHeight);
         }
-        mMaxAllowedKeyguardNotifications = res.getInteger(
-                R.integer.keyguard_max_notification_count);
 
         if (DEBUG) Log.v(TAG, "defineSlots");
     }
@@ -3058,12 +2691,12 @@
         if (visibleToUser) {
             boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
             boolean clearNotificationEffects =
-                    !isPresenterFullyCollapsed() &&
+                    !mPresenter.isPresenterFullyCollapsed() &&
                             (mState == StatusBarState.SHADE
                                     || mState == StatusBarState.SHADE_LOCKED);
             int notificationLoad = mEntryManager.getNotificationData().getActiveNotifications()
                     .size();
-            if (pinnedHeadsUp && isPresenterFullyCollapsed()) {
+            if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
                 notificationLoad = 1;
             }
             final int finalNotificationLoad = notificationLoad;
@@ -3298,13 +2931,8 @@
         }
     }
 
-    @Override
-    public boolean isPresenterFullyCollapsed() {
-        return mNotificationPanel.isFullyCollapsed();
-    }
-
     public void showKeyguard() {
-        mKeyguardRequested = true;
+        mStatusBarStateController.setKeyguardRequested(true);
         mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
         mPendingRemoteInputView = null;
         updateIsKeyguard();
@@ -3312,11 +2940,12 @@
     }
 
     public boolean hideKeyguard() {
-        mKeyguardRequested = false;
+        mStatusBarStateController.setKeyguardRequested(false);
         return updateIsKeyguard();
     }
 
     /**
+     * stop(tag)
      * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER.
      */
     public boolean isFullScreenUserSwitcherState() {
@@ -3333,7 +2962,8 @@
         // turned off fully.
         boolean keyguardForDozing = mDozingRequested &&
                 (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
-        boolean shouldBeKeyguard = (mKeyguardRequested || keyguardForDozing) && !wakeAndUnlocking;
+        boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
+                || keyguardForDozing) && !wakeAndUnlocking;
         if (keyguardForDozing) {
             updatePanelExpansionForKeyguard();
         }
@@ -3385,13 +3015,7 @@
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
         mLaunchTransitionFadingAway = false;
-        updateMediaMetaData(true /* metaDataChanged */, true);
-    }
-
-    public boolean isCollapsing() {
-        return mNotificationPanel.isCollapsing()
-                || mActivityLaunchAnimator.isAnimationPending()
-                || mActivityLaunchAnimator.isAnimationRunning();
+        mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
     }
 
     public void addPostCollapseAction(Runnable r) {
@@ -3415,12 +3039,13 @@
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         mLaunchTransitionEndRunnable = endRunnable;
         Runnable hideRunnable = () -> {
+            mKeyguardMonitor.setLaunchTransitionFadingAway(true);
             mLaunchTransitionFadingAway = true;
             if (beforeFading != null) {
                 beforeFading.run();
             }
             updateScrimController();
-            updateMediaMetaData(false, true);
+            mPresenter.updateMediaMetaData(false, true);
             mNotificationPanel.setAlpha(1);
             mNotificationPanel.animate()
                     .alpha(0)
@@ -3505,9 +3130,8 @@
             mLockscreenUserManager.updatePublicMode();
             mEntryManager.updateNotifications();
         }
-        View viewToClick = null;
         if (mStatusBarStateController.leaveOpenOnKeyguardHide()) {
-            if (!mKeyguardRequested) {
+            if (!mStatusBarStateController.isKeyguardRequested()) {
                 mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
             }
             long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
@@ -3516,10 +3140,6 @@
                 mDraggedDownRow.setUserLocked(false);
                 mDraggedDownRow = null;
             }
-            if (!mKeyguardRequested) {
-                viewToClick = mPendingRemoteInputView;
-                mPendingRemoteInputView = null;
-            }
 
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
@@ -3530,10 +3150,6 @@
             instantCollapseNotificationPanel();
         }
 
-        if (viewToClick != null && viewToClick.isAttachedToWindow()) {
-            viewToClick.callOnClick();
-        }
-
         // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
         // visibilities so next time we open the panel we know the correct height already.
         if (mQSPanel != null) {
@@ -3575,7 +3191,7 @@
         mCommandQueue.appTransitionStarting(startTime + fadeoutDuration
                         - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                 LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
-        recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
+        mCommandQueue.recomputeDisableFlags(fadeoutDuration > 0 /* animate */);
         mCommandQueue.appTransitionStarting(
                     startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION,
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
@@ -3617,7 +3233,8 @@
 
         mDozeScrimController.setDozing(mDozing);
         mKeyguardIndicationController.setDozing(mDozing);
-        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
+        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation,
+                mDozeServiceHost.wasPassivelyInterrupted());
         mNotificationLogger.setDozing(mDozing);
         mGroupManager.setDozing(mDozing);
         updateQsExpansionEnabled();
@@ -3696,15 +3313,43 @@
         }
     }
 
-    protected void showBouncer(boolean scrimmed) {
+    @Override
+    public void showBouncer(boolean scrimmed) {
         mStatusBarKeyguardViewManager.showBouncer(scrimmed);
     }
 
-    private void instantExpandNotificationsPanel() {
+    @Override
+    public void instantExpandNotificationsPanel() {
         // Make our window larger and the panel expanded.
         makeExpandedVisible(true);
         mNotificationPanel.expand(false /* animate */);
-        recomputeDisableFlags(false /* animate */);
+        mCommandQueue.recomputeDisableFlags(false /* animate */);
+    }
+
+    @Override
+    public boolean closeShadeIfOpen() {
+        if (!mNotificationPanel.isFullyCollapsed()) {
+            mCommandQueue.animateCollapsePanels(
+                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
+            visibilityChanged(false);
+            mAssistManager.hideAssist();
+        }
+        return false;
+    }
+
+    @Override
+    public void postOnShadeExpanded(Runnable executable) {
+        mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        if (getStatusBarWindow().getHeight() != getStatusBarHeight()) {
+                            mNotificationPanel.getViewTreeObserver()
+                                    .removeOnGlobalLayoutListener(this);
+                            mNotificationPanel.post(executable);
+                        }
+                    }
+                });
     }
 
     private void instantCollapseNotificationPanel() {
@@ -3713,25 +3358,7 @@
     }
 
     @Override
-    public void onActivated(ActivatableNotificationView view) {
-        onActivated((View) view);
-        mNotificationPanel.setActivatedChild(view);
-    }
-
-    public void onActivated(View view) {
-        mLockscreenGestureLogger.write(
-                MetricsEvent.ACTION_LS_NOTE,
-                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
-        mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
-        ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild();
-        if (previousView != null) {
-            previousView.makeInactive(true /* animate */);
-        }
-    }
-
-    @Override
     public void onStatePreChange(int oldState, int newState) {
-
         // If we're visible and switched to SHADE_LOCKED (the user dragged
         // down on the lockscreen), clear notification LED, vibration,
         // ringing.
@@ -3756,9 +3383,6 @@
         Trace.beginSection("StatusBar#updateKeyguardState");
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardIndicationController.setVisible(true);
-            boolean dozingAnimated = mDozingRequested
-                    && DozeParameters.getInstance(mContext).shouldControlScreenOff();
-            mNotificationPanel.resetViews(dozingAnimated);
             if (mKeyguardUserSwitcher != null) {
                 mKeyguardUserSwitcher.setKeyguard(true,
                         mStatusBarStateController.fromShadeLocked());
@@ -3782,7 +3406,7 @@
         updateDozingState();
         checkBarModes();
         updateScrimController();
-        updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
+        mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
         mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
                 mUnlockMethodCache.isMethodSecure(),
                 mStatusBarKeyguardViewManager.isOccluded());
@@ -3790,14 +3414,48 @@
     }
 
     @Override
-    public void onActivationReset(ActivatableNotificationView view) {
-        if (view == mNotificationPanel.getActivatedChild()) {
-            mNotificationPanel.setActivatedChild(null);
-            onActivationReset((View)view);
+    public void onDozingChanged(boolean isDozing) {
+        Trace.beginSection("StatusBar#updateDozing");
+        mDozing = isDozing;
+
+        // Collapse the notification panel if open
+        boolean dozingAnimated = mDozingRequested
+                && DozeParameters.getInstance(mContext).shouldControlScreenOff();
+        mNotificationPanel.resetViews(dozingAnimated);
+
+        updateQsExpansionEnabled();
+        mKeyguardViewMediator.setAodShowing(mDozing);
+
+        //TODO: make these folks listeners of StatusBarStateController.onDozingChanged
+        mStatusBarWindowController.setDozing(mDozing);
+        mStatusBarKeyguardViewManager.setDozing(mDozing);
+        if (mAmbientIndicationContainer instanceof DozeReceiver) {
+            ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
         }
+
+        mEntryManager.updateNotifications();
+        updateDozingState();
+        updateScrimController();
+        updateReportRejectedTouchVisibility();
+        Trace.endSection();
     }
 
-    public void onActivationReset(View view) {
+    private void updateDozing() {
+        // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
+        boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
+                || mBiometricUnlockController.getMode()
+                == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+        // When in wake-and-unlock we may not have received a change to mState
+        // but we still should not be dozing, manually set to false.
+        if (mBiometricUnlockController.getMode() ==
+                BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
+            dozing = false;
+        }
+
+        mStatusBarStateController.setIsDozing(dozing);
+    }
+
+    public void onActivationReset() {
         mKeyguardIndicationController.hideTransientIndication();
     }
 
@@ -3807,7 +3465,7 @@
 
     public void onClosingFinished() {
         runPostCollapseRunnables();
-        if (!isPresenterFullyCollapsed()) {
+        if (!mPresenter.isPresenterFullyCollapsed()) {
             // if we set it not to be focusable when collapsing, we have to undo it when we aborted
             // the closing
             mStatusBarWindowController.setStatusBarFocusable(true);
@@ -3847,22 +3505,6 @@
         }
     }
 
-    @Override
-    public int getMaxNotificationsWhileLocked(boolean recompute) {
-        if (recompute) {
-            mMaxKeyguardNotifications = Math.max(1,
-                    mNotificationPanel.computeMaxKeyguardNotifications(
-                            mMaxAllowedKeyguardNotifications));
-            return mMaxKeyguardNotifications;
-        }
-        return mMaxKeyguardNotifications;
-    }
-
-    @Override
-    public void onUpdateRowStates() {
-        mNotificationPanel.onUpdateRowStates();
-    }
-
     // TODO: Figure out way to remove these.
     public NavigationBarView getNavigationBarView() {
         return (mNavigationBar != null ? (NavigationBarView) mNavigationBar.getView() : null);
@@ -3919,184 +3561,6 @@
         }
     }
 
-    public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
-        mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
-        dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
-    }
-
-    @Override
-    public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
-        mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
-        showBouncer(true /* scrimmed */);
-        mPendingRemoteInputView = clicked;
-    }
-
-    @Override
-    public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
-            View clickedView) {
-        if (isKeyguardShowing()) {
-            onLockedRemoteInput(row, clickedView);
-        } else {
-            row.setUserExpanded(true);
-            row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
-        }
-    }
-
-    @Override
-    public boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent) {
-        // Skip remote input as doing so will expand the notification shade.
-        return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
-    }
-
-    @Override
-    public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
-            Intent fillInIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) {
-        final boolean isActivity = pendingIntent.isActivity();
-        if (isActivity) {
-            final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
-                    mContext, pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
-            dismissKeyguardThenExecute(() -> {
-                try {
-                    ActivityManager.getService().resumeAppSwitches();
-                } catch (RemoteException e) {
-                }
-
-                boolean handled = defaultHandler.handleClick();
-
-                // close the shade if it was open
-                if (handled && !mNotificationPanel.isFullyCollapsed()) {
-                    animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
-                    visibilityChanged(false);
-                    mAssistManager.hideAssist();
-
-                    // Wait for activity start.
-                    return true;
-                } else {
-                    return false;
-                }
-
-            }, afterKeyguardGone);
-            return true;
-        } else {
-            return defaultHandler.handleClick();
-        }
-    }
-
-    protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
-            String notificationKey) {
-        // Clear pending remote view, as we do not want to trigger pending remote input view when
-        // it's called by other code
-        mPendingWorkRemoteInputView = null;
-        // Begin old BaseStatusBar.startWorkChallengeIfNecessary.
-        final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
-                null, userId);
-        if (newIntent == null) {
-            return false;
-        }
-        final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
-        callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
-        callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
-        callBackIntent.setPackage(mContext.getPackageName());
-
-        PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
-                mContext,
-                0,
-                callBackIntent,
-                PendingIntent.FLAG_CANCEL_CURRENT |
-                        PendingIntent.FLAG_ONE_SHOT |
-                        PendingIntent.FLAG_IMMUTABLE);
-        newIntent.putExtra(
-                Intent.EXTRA_INTENT,
-                callBackPendingIntent.getIntentSender());
-        try {
-            ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
-                    null /*options*/);
-        } catch (RemoteException ex) {
-            // ignore
-        }
-        return true;
-        // End old BaseStatusBar.startWorkChallengeIfNecessary.
-    }
-
-    @Override
-    public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
-            View clicked) {
-        // Collapse notification and show work challenge
-        animateCollapsePanels();
-        startWorkChallengeIfNecessary(userId, null, null);
-        // Add pending remote input view after starting work challenge, as starting work challenge
-        // will clear all previous pending review view
-        mPendingWorkRemoteInputView = clicked;
-    }
-
-    @Override
-    public void onWorkChallengeChanged() {
-        if (mPendingWorkRemoteInputView != null
-                && !mLockscreenUserManager.isAnyProfilePublicMode()) {
-            // Expand notification panel and the notification row, then click on remote input view
-            final Runnable clickPendingViewRunnable = () -> {
-                final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
-                if (pendingWorkRemoteInputView == null) {
-                    return;
-                }
-
-                // Climb up the hierarchy until we get to the container for this row.
-                ViewParent p = pendingWorkRemoteInputView.getParent();
-                while (!(p instanceof ExpandableNotificationRow)) {
-                    if (p == null) {
-                        return;
-                    }
-                    p = p.getParent();
-                }
-
-                final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
-                ViewParent viewParent = row.getParent();
-                if (viewParent instanceof NotificationStackScrollLayout) {
-                    final NotificationStackScrollLayout scrollLayout =
-                            (NotificationStackScrollLayout) viewParent;
-                    row.makeActionsVisibile();
-                    row.post(() -> {
-                        final Runnable finishScrollingCallback = () -> {
-                            mPendingWorkRemoteInputView.callOnClick();
-                            mPendingWorkRemoteInputView = null;
-                            scrollLayout.setFinishScrollingCallback(null);
-                        };
-                        if (scrollLayout.scrollTo(row)) {
-                            // It scrolls! So call it when it's finished.
-                            scrollLayout.setFinishScrollingCallback(finishScrollingCallback);
-                        } else {
-                            // It does not scroll, so call it now!
-                            finishScrollingCallback.run();
-                        }
-                    });
-                }
-            };
-            mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
-                    new ViewTreeObserver.OnGlobalLayoutListener() {
-                        @Override
-                        public void onGlobalLayout() {
-                            if (mNotificationPanel.mStatusBar.getStatusBarWindow()
-                                    .getHeight() != mNotificationPanel.mStatusBar
-                                            .getStatusBarHeight()) {
-                                mNotificationPanel.getViewTreeObserver()
-                                        .removeOnGlobalLayoutListener(this);
-                                mNotificationPanel.post(clickPendingViewRunnable);
-                            }
-                        }
-                    });
-            instantExpandNotificationsPanel();
-        }
-    }
-
-    @Override
-    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
-        mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
-        if (mState == StatusBarState.KEYGUARD && nowExpanded) {
-            goToLockedShade(clickedEntry.row);
-        }
-    }
-
     /**
      * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
      */
@@ -4110,7 +3574,7 @@
         mBouncerShowing = bouncerShowing;
         if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
         updateHideIconsForBouncer(true /* animate */);
-        recomputeDisableFlags(true /* animate */);
+        mCommandQueue.recomputeDisableFlags(true /* animate */);
         updateScrimController();
     }
 
@@ -4165,7 +3629,6 @@
             mAmbientPulseManager.releaseAllImmediately();
             mVisualStabilityManager.setScreenOn(true);
             mNotificationPanel.setTouchAndAnimationDisabled(false);
-            mDozeServiceHost.stopDozing();
             updateVisibleToUser();
             updateIsKeyguard();
             updateScrimController();
@@ -4235,25 +3698,6 @@
     }
 
     @Override
-    public void wakeUpIfDozing(long time, View where) {
-        if (mDozing) {
-            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-            pm.wakeUp(time, "com.android.systemui:NODOZE");
-            mWakeUpComingFromTouch = true;
-            where.getLocationInWindow(mTmpInt2);
-            mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
-                    mTmpInt2[1] + where.getHeight() / 2);
-            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
-            mFalsingManager.onScreenOnFromTouch();
-        }
-    }
-
-    @Override
-    public boolean isDeviceLocked(int userId) {
-        return mKeyguardManager.isDeviceLocked(userId);
-    }
-
-    @Override
     public void appTransitionCancelled() {
         getComponent(Divider.class).onAppTransitionFinished();
     }
@@ -4341,34 +3785,6 @@
         updateScrimController();
     }
 
-    private void updateDozing() {
-        Trace.beginSection("StatusBar#updateDozing");
-        // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
-        boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
-                || mBiometricUnlockController.getMode()
-                        == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-        // When in wake-and-unlock we may not have received a change to mState
-        // but we still should not be dozing, manually set to false.
-        if (mBiometricUnlockController.getMode() ==
-                mBiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
-            dozing = false;
-        }
-        if (mDozing != dozing) {
-            mDozing = dozing;
-            mKeyguardViewMediator.setAodShowing(mDozing);
-            mStatusBarWindowController.setDozing(mDozing);
-            mStatusBarKeyguardViewManager.setDozing(mDozing);
-            if (mAmbientIndicationContainer instanceof DozeReceiver) {
-                ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
-            }
-            mEntryManager.updateNotifications();
-            updateDozingState();
-            updateScrimController();
-            updateReportRejectedTouchVisibility();
-        }
-        Trace.endSection();
-    }
-
     @VisibleForTesting
     void updateScrimController() {
         Trace.beginSection("StatusBar#updateScrimController");
@@ -4391,6 +3807,9 @@
             // FLAG_DISMISS_KEYGUARD_ACTIVITY.
             ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
                     ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
+            if (mNotificationPanel.isSemiAwake()) {
+                state = ScrimState.DARK_KEYGUARD;
+            }
             mScrimController.transitionTo(state);
         } else if (isInLaunchTransition() || mLaunchCameraOnScreenTurningOn
                 || launchingAffordanceWithPreview) {
@@ -4402,7 +3821,8 @@
         } else if (mDozing) {
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !wakeAndUnlocking) {
-            mScrimController.transitionTo(ScrimState.KEYGUARD);
+            mScrimController.transitionTo(mNotificationPanel.isSemiAwake()
+                    ? ScrimState.DARK_KEYGUARD : ScrimState.KEYGUARD);
         } else {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
@@ -4422,6 +3842,7 @@
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
         private boolean mIgnoreTouchWhilePulsing;
+        private boolean mPassivelyInterrupted;
 
         @Override
         public String toString() {
@@ -4505,6 +3926,11 @@
         }
 
         @Override
+        public void setPassiveInterrupt(boolean passiveInterrupt) {
+            mPassivelyInterrupted = passiveInterrupt;
+        }
+
+        @Override
         public void onIgnoreTouchWhilePulsing(boolean ignore) {
             if (ignore != mIgnoreTouchWhilePulsing) {
                 DozeLog.tracePulseTouchDisabledByProx(mContext, ignore);
@@ -4623,6 +4049,10 @@
         public boolean shouldAnimateScreenOff() {
             return mAnimateScreenOff;
         }
+
+        public boolean wasPassivelyInterrupted() {
+            return mPassivelyInterrupted;
+        }
     }
 
     public boolean shouldIgnoreTouch() {
@@ -4645,8 +4075,6 @@
 
     protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
 
-    private AboveShelfObserver mAboveShelfObserver;
-
     // handling reordering
     protected VisualStabilityManager mVisualStabilityManager;
 
@@ -4664,7 +4092,6 @@
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     protected KeyguardManager mKeyguardManager;
-    private LockPatternUtils mLockPatternUtils;
     private DeviceProvisionedController mDeviceProvisionedController
             = Dependency.get(DeviceProvisionedController.class);
 
@@ -4683,28 +4110,10 @@
 
     protected AssistManager mAssistManager;
 
-    protected boolean mVrMode;
-
     public boolean isDeviceInteractive() {
         return mDeviceInteractive;
     }
 
-    @Override  // NotificationData.Environment
-    public boolean isDeviceProvisioned() {
-        return mDeviceProvisionedController.isDeviceProvisioned();
-    }
-
-    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
-        @Override
-        public void onVrStateChanged(boolean enabled) {
-            mVrMode = enabled;
-        }
-    };
-
-    public boolean isDeviceInVrMode() {
-        return mVrMode;
-    }
-
     private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -4730,182 +4139,13 @@
     };
 
     @Override
-    public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
-        RemoteInputController controller = mRemoteInputManager.getController();
-        if (controller.isRemoteInputActive(row.getEntry())
-                && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
-            // We have an active remote input typed and the user clicked on the notification.
-            // this was probably unintentional, so we're closing the edit text instead.
-            controller.closeRemoteInputs();
-            return;
-        }
-        Notification notification = sbn.getNotification();
-        final PendingIntent intent = notification.contentIntent != null
-                ? notification.contentIntent
-                : notification.fullScreenIntent;
-        final String notificationKey = sbn.getKey();
-
-        boolean isActivityIntent = intent.isActivity();
-        final boolean afterKeyguardGone = isActivityIntent
-                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
-                mLockscreenUserManager.getCurrentUserId());
-        final boolean wasOccluded = mIsOccluded;
-        boolean showOverLockscreen = mStatusBarKeyguardViewManager.isShowing()
-                && PreviewInflater.wouldShowOverLockscreen(mContext,
-                intent.getIntent(),
-                mLockscreenUserManager.getCurrentUserId());
-        OnDismissAction postKeyguardAction = () -> {
-            // TODO: Some of this code may be able to move to NotificationEntryManager.
-            if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
-                // Release the HUN notification to the shade.
-
-                if (isPresenterFullyCollapsed()) {
-                    HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
-                }
-                //
-                // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
-                // become canceled shortly by NoMan, but we can't assume that.
-                mHeadsUpManager.removeNotification(sbn.getKey(),
-                        true /* releaseImmediately */);
-            }
-            StatusBarNotification parentToCancel = null;
-            if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
-                StatusBarNotification summarySbn =
-                        mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
-                if (shouldAutoCancel(summarySbn)) {
-                    parentToCancel = summarySbn;
-                }
-            }
-            final StatusBarNotification parentToCancelFinal = parentToCancel;
-            final Runnable runnable = () -> {
-                try {
-                    // The intent we are sending is for the application, which
-                    // won't have permission to immediately start an activity after
-                    // the user switches to home.  We know it is safe to do at this
-                    // point, so make sure new activity switches are now allowed.
-                    ActivityManager.getService().resumeAppSwitches();
-                } catch (RemoteException e) {
-                }
-                int launchResult = ActivityManager.START_CANCELED;
-                if (intent != null) {
-                    // If we are launching a work activity and require to launch
-                    // separate work challenge, we defer the activity action and cancel
-                    // notification until work challenge is unlocked.
-                    if (isActivityIntent) {
-                        final int userId = intent.getCreatorUserHandle().getIdentifier();
-                        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
-                                && mKeyguardManager.isDeviceLocked(userId)) {
-                            // TODO(b/28935539): should allow certain activities to
-                            // bypass work challenge
-                            if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(),
-                                    notificationKey)) {
-                                // Show work challenge, do not run PendingIntent and
-                                // remove notification
-                                collapseOnMainThread();
-                                return;
-                            }
-                        }
-                    }
-                    Intent fillInIntent = null;
-                    Entry entry = row.getEntry();
-                    CharSequence remoteInputText = null;
-                    if (!TextUtils.isEmpty(entry.remoteInputText)) {
-                        remoteInputText = entry.remoteInputText;
-                    }
-                    if (!TextUtils.isEmpty(remoteInputText)
-                            && !controller.isSpinning(entry.key)) {
-                        fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
-                                remoteInputText.toString());
-                    }
-                    RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
-                            row, wasOccluded);
-                    try {
-                        if (adapter != null) {
-                            ActivityTaskManager.getService()
-                                    .registerRemoteAnimationForNextActivityStart(
-                                            intent.getCreatorPackage(), adapter);
-                        }
-                        launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
-                                null, null, getActivityOptions(adapter));
-                        mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
-                    } catch (RemoteException | PendingIntent.CanceledException e) {
-                        // the stack trace isn't very helpful here.
-                        // Just log the exception message.
-                        Log.w(TAG, "Sending contentIntent failed: " + e);
-
-                        // TODO: Dismiss Keyguard.
-                    }
-                    if (isActivityIntent) {
-                        mAssistManager.hideAssist();
-                    }
-                }
-                if (shouldCollapse()) {
-                    collapseOnMainThread();
-                }
-
-                final int count =
-                        mEntryManager.getNotificationData().getActiveNotifications().size();
-                final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
-                final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
-                        rank, count, true);
-                try {
-                    mBarService.onNotificationClick(notificationKey, nv);
-                } catch (RemoteException ex) {
-                    // system process is dead if we're here.
-                }
-                if (parentToCancelFinal != null) {
-                    removeNotification(parentToCancelFinal);
-                }
-                if (shouldAutoCancel(sbn)
-                        || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
-                                notificationKey)) {
-                    // Automatically remove all notifications that we may have kept around longer
-                    removeNotification(sbn);
-                }
-
-                mIsCollapsingToShowActivityOverLockscreen = false;
-            };
-
-            if (showOverLockscreen) {
-                addPostCollapseAction(runnable);
-                collapsePanel(true /* animate */);
-            } else if (mStatusBarKeyguardViewManager.isShowing()
-                    && mStatusBarKeyguardViewManager.isOccluded()) {
-                mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
-                collapsePanel(true /* animate */);
-            } else {
-                new Thread(runnable).start();
-            }
-
-            return !mNotificationPanel.isFullyCollapsed();
-        };
-        if (showOverLockscreen) {
-            mIsCollapsingToShowActivityOverLockscreen = true;
-            postKeyguardAction.onDismiss();
-        } else {
-            dismissKeyguardThenExecute(postKeyguardAction, afterKeyguardGone);
-        }
-    }
-
-    private void collapseOnMainThread() {
-        if (Looper.getMainLooper().isCurrentThread()) {
-            collapsePanel();
-        } else {
-            Dependency.get(Dependency.MAIN_HANDLER).post(this::collapsePanel);
-        }
-    }
-
-    private boolean shouldCollapse() {
-        return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending();
-    }
-
     public void collapsePanel(boolean animate) {
         if (animate) {
             boolean willCollapse = collapsePanel();
             if (!willCollapse) {
                 runPostCollapseRunnables();
             }
-        } else if (!isPresenterFullyCollapsed()) {
+        } else if (!mPresenter.isPresenterFullyCollapsed()) {
             instantCollapseNotificationPanel();
             visibilityChanged(false);
         } else {
@@ -4913,7 +4153,8 @@
         }
     }
 
-    private boolean collapsePanel() {
+    @Override
+    public boolean collapsePanel() {
         if (!mNotificationPanel.isFullyCollapsed()) {
             // close the shade if it was open
             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */,
@@ -4926,59 +4167,8 @@
         }
     }
 
-    private void removeNotification(StatusBarNotification notification) {
-        // We have to post it to the UI thread for synchronization
-        mHandler.post(() -> {
-            Runnable removeRunnable =
-                    () -> mEntryManager.performRemoveNotification(notification);
-            if (isCollapsing()) {
-                // To avoid lags we're only performing the remove
-                // after the shade was collapsed
-                addPostCollapseAction(removeRunnable);
-            } else {
-                removeRunnable.run();
-            }
-        });
-    }
-
     protected NotificationListener mNotificationListener;
 
-    @Override  // NotificationData.Environment
-    public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
-        final int notificationUserId = n.getUserId();
-        if (DEBUG && MULTIUSER_DEBUG) {
-            Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n,
-                    mLockscreenUserManager.getCurrentUserId(), notificationUserId));
-        }
-        return mLockscreenUserManager.isCurrentProfile(notificationUserId);
-    }
-
-    @Override
-    public NotificationGroupManager getGroupManager() {
-        return mGroupManager;
-    }
-
-    @Override
-    public void startNotificationGutsIntent(final Intent intent, final int appUid,
-            ExpandableNotificationRow row) {
-        dismissKeyguardThenExecute(() -> {
-            AsyncTask.execute(() -> {
-                int launchResult = TaskStackBuilder.create(mContext)
-                        .addNextIntentWithParentStack(intent)
-                        .startActivities(getActivityOptions(
-                                mActivityLaunchAnimator.getLaunchAnimation(row, mIsOccluded)),
-                                new UserHandle(UserHandle.getUserId(appUid)));
-                mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
-                if (shouldCollapse()) {
-                    // Putting it back on the main thread, since we're touching views
-                    mStatusBarWindow.post(() -> animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
-                }
-            });
-            return true;
-        }, false /* afterKeyguardGone */);
-    }
-
     public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
         if (snoozeOption.getSnoozeCriterion() != null) {
             mNotificationListener.snoozeNotification(sbn.getKey(),
@@ -5039,7 +4229,7 @@
             // Immediately update the icon hidden state, since that should only apply if we're
             // staying fullscreen.
             mWereIconsJustHidden = false;
-            recomputeDisableFlags(true);
+            mCommandQueue.recomputeDisableFlags(true);
         }
         updateHideIconsForBouncer(true /* animate */);
     }
@@ -5052,24 +4242,6 @@
         KeyboardShortcuts.dismiss();
     }
 
-    @Override  // NotificationData.Environment
-    public boolean shouldHideNotifications(int userId) {
-        return mLockscreenUserManager.shouldHideNotifications(userId);
-    }
-
-    @Override // NotificationDate.Environment
-    public boolean shouldHideNotifications(String key) {
-        return mLockscreenUserManager.shouldHideNotifications(key);
-    }
-
-    /**
-     * Returns true if we're on a secure lockscreen.
-     */
-    @Override  // NotificationData.Environment
-    public boolean isSecurelyLocked(int userId) {
-        return mLockscreenUserManager.isLockscreenPublicMode(userId);
-    }
-
     /**
      * Called when the notification panel layouts
      */
@@ -5081,8 +4253,8 @@
         if (mState == StatusBarState.KEYGUARD) {
             // Since the number of notifications is determined based on the height of the view, we
             // need to update them.
-            int maxBefore = getMaxNotificationsWhileLocked(false /* recompute */);
-            int maxNotifications = getMaxNotificationsWhileLocked(true /* recompute */);
+            int maxBefore = mPresenter.getMaxNotificationsWhileLocked(false /* recompute */);
+            int maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
             if (maxBefore != maxNotifications) {
                 mViewHierarchyManager.updateRowStates();
             }
@@ -5090,7 +4262,7 @@
     }
 
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
-        if (!isDeviceProvisioned()) return;
+        if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
 
         final boolean afterKeyguardGone = intent.isActivity()
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
@@ -5124,18 +4296,7 @@
         }, afterKeyguardGone);
     }
 
-    private boolean shouldAutoCancel(StatusBarNotification sbn) {
-        int flags = sbn.getNotification().flags;
-        if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
-            return false;
-        }
-        if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
-            return false;
-        }
-        return true;
-    }
-
-    protected Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
+    public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
         ActivityOptions options;
         if (animationAdapter != null) {
             options = ActivityOptions.makeRemoteAnimation(animationAdapter);
@@ -5252,29 +4413,4 @@
     public NotificationGutsManager getGutsManager() {
         return mGutsManager;
     }
-
-    @Override
-    public boolean isPresenterLocked() {
-        return mState == StatusBarState.KEYGUARD;
-    }
-
-    @Override
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    private final NotificationInfo.CheckSaveListener mCheckSaveListener =
-            (Runnable saveImportance, StatusBarNotification sbn) -> {
-                // If the user has security enabled, show challenge if the setting is changed.
-                if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
-                        && (mState == StatusBarState.KEYGUARD ||
-                                mState == StatusBarState.SHADE_LOCKED)) {
-                    onLockedNotificationImportanceChange(() -> {
-                        saveImportance.run();
-                        return true;
-                    });
-                } else {
-                    saveImportance.run();
-                }
-            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ac3608b..c560301 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
 
@@ -43,6 +43,7 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
@@ -124,6 +125,8 @@
     // Dismiss action to be launched when we stop dozing or the keyguard is gone.
     private DismissWithActionRequest mPendingWakeupAction;
     private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+    private final NotificationMediaManager mMediaManager =
+            Dependency.get(NotificationMediaManager.class);
 
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -381,7 +384,7 @@
         boolean isOccluding = !mOccluded && occluded;
         mOccluded = occluded;
         if (mShowing) {
-            mStatusBar.updateMediaMetaData(false, animate && !occluded);
+            mMediaManager.updateMediaMetaData(false, animate && !occluded);
         }
         mStatusBarWindowController.setKeyguardOccluded(occluded);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
new file mode 100644
index 0000000..edfc049
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.Dependency.MAIN_HANDLER;
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
+import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
+import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
+import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
+
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.TaskStackBuilder;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.AsyncTask;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.TextView;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.Dependency;
+import com.android.systemui.InitController;
+import com.android.systemui.R;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.AboveShelfObserver;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
+import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.PreviewInflater;
+
+public class StatusBarNotificationPresenter implements NotificationPresenter {
+
+    private final LockscreenGestureLogger mLockscreenGestureLogger =
+            Dependency.get(LockscreenGestureLogger.class);
+
+    private static final String TAG = "StatusBarNotificationPresenter";
+
+    private final ShadeController mShadeController = Dependency.get(ShadeController.class);
+    private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+    private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
+    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+    private final NotificationViewHierarchyManager mViewHierarchyManager =
+            Dependency.get(NotificationViewHierarchyManager.class);
+    private final NotificationLockscreenUserManager mLockscreenUserManager =
+            Dependency.get(NotificationLockscreenUserManager.class);
+    private final StatusBarStateController mStatusBarStateController =
+            Dependency.get(StatusBarStateController.class);
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
+    private final NotificationMediaManager mMediaManager =
+            Dependency.get(NotificationMediaManager.class);
+    private final NotificationRemoteInputManager mRemoteInputManager =
+            Dependency.get(NotificationRemoteInputManager.class);
+    private final NotificationGroupManager mGroupManager =
+            Dependency.get(NotificationGroupManager.class);
+    private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback =
+            (StatusBarRemoteInputCallback) Dependency.get(Callback.class);
+    protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
+
+    private final NotificationPanelView mNotificationPanel;
+    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final AboveShelfObserver mAboveShelfObserver;
+    private final DozeScrimController mDozeScrimController;
+    private final ScrimController mScrimController;
+    private final Context mContext;
+    private final CommandQueue mCommandQueue;
+
+    private final AccessibilityManager mAccessibilityManager;
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardManager mKeyguardManager;
+    private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final int mMaxAllowedKeyguardNotifications;
+    private final IStatusBarService mBarService;
+    private boolean mReinflateNotificationsOnUserSwitched;
+    private final UnlockMethodCache mUnlockMethodCache;
+    private TextView mNotificationPanelDebugText;
+
+    protected boolean mVrMode;
+    private int mMaxKeyguardNotifications;
+    private boolean mIsCollapsingToShowActivityOverLockscreen;
+
+    public StatusBarNotificationPresenter(Context context, NotificationPanelView panel,
+            HeadsUpManagerPhone headsUp, StatusBarWindowView statusBarWindow,
+            ViewGroup stackScroller, DozeScrimController dozeScrimController,
+            ScrimController scrimController,
+            ActivityLaunchAnimator.Callback launchAnimatorCallback) {
+        mContext = context;
+        mNotificationPanel = panel;
+        mHeadsUpManager = headsUp;
+        mCommandQueue = getComponent(context, CommandQueue.class);
+        mAboveShelfObserver = new AboveShelfObserver(stackScroller);
+        mAboveShelfObserver.setListener(statusBarWindow.findViewById(
+                R.id.notification_container_parent));
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        mDozeScrimController = dozeScrimController;
+        mScrimController = scrimController;
+        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
+        mLockPatternUtils = new LockPatternUtils(context);
+        mKeyguardManager = context.getSystemService(KeyguardManager.class);
+        mMaxAllowedKeyguardNotifications = context.getResources().getInteger(
+                R.integer.keyguard_max_notification_count);
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mActivityLaunchAnimator = new ActivityLaunchAnimator(statusBarWindow,
+                launchAnimatorCallback,
+                mNotificationPanel,
+                (NotificationListContainer) stackScroller);
+
+        if (MULTIUSER_DEBUG) {
+            mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
+            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
+        }
+
+        IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
+                Context.VR_SERVICE));
+        if (vrManager != null) {
+            try {
+                vrManager.registerListener(mVrStateCallbacks);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+            }
+        }
+        mRemoteInputManager.setUpWithPresenter(this,
+                Dependency.get(NotificationRemoteInputManager.Callback.class),
+                mNotificationPanel.createRemoteInputDelegate());
+        mRemoteInputManager.getController().addCallback(
+                Dependency.get(StatusBarWindowController.class));
+
+        NotificationListContainer notifListContainer = (NotificationListContainer) stackScroller;
+        Dependency.get(InitController.class).addPostInitTask(() -> {
+            mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
+            mEntryManager.setUpWithPresenter(this, notifListContainer, this, mHeadsUpManager);
+            mLockscreenUserManager.setUpWithPresenter(this);
+            mMediaManager.setUpWithPresenter(this);
+            Dependency.get(NotificationGutsManager.class).setUpWithPresenter(this,
+                    notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
+
+            onUserSwitched(mLockscreenUserManager.getCurrentUserId());
+        });
+    }
+
+    public void onDensityOrFontScaleChanged() {
+        if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+            mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+        } else {
+            mReinflateNotificationsOnUserSwitched = true;
+        }
+    }
+
+    @Override
+    public ActivityLaunchAnimator getActivityLaunchAnimator() {
+        return mActivityLaunchAnimator;
+    }
+
+    @Override
+    public boolean isCollapsing() {
+        return mNotificationPanel.isCollapsing()
+                || mActivityLaunchAnimator.isAnimationPending()
+                || mActivityLaunchAnimator.isAnimationRunning();
+    }
+
+    @Override
+    public boolean isCollapsingToShowActivityOverLockscreen() {
+        return mIsCollapsingToShowActivityOverLockscreen;
+    }
+
+    @Override
+    public void onPerformRemoveNotification(StatusBarNotification n) {
+        if (mNotificationPanel.hasPulsingNotifications() &&
+                    !mAmbientPulseManager.hasNotifications()) {
+            // We were showing a pulse for a notification, but no notifications are pulsing anymore.
+            // Finish the pulse.
+            mDozeScrimController.pulseOutNow();
+        }
+    }
+
+    @Override
+    public void updateNotificationViews() {
+        // The function updateRowStates depends on both of these being non-null, so check them here.
+        // We may be called before they are set from DeviceProvisionedController's callback.
+        if (mScrimController == null) return;
+
+        // Do not modify the notifications during collapse.
+        if (isCollapsing()) {
+            mShadeController.addPostCollapseAction(this::updateNotificationViews);
+            return;
+        }
+
+        mViewHierarchyManager.updateNotificationViews();
+
+        mNotificationPanel.updateNotificationViews();
+    }
+
+    @Override
+    public void onNotificationAdded(Entry shadeEntry) {
+        // Recalculate the position of the sliding windows and the titles.
+        mShadeController.updateAreThereNotifications();
+    }
+
+    @Override
+    public void onNotificationUpdated(StatusBarNotification notification) {
+        mShadeController.updateAreThereNotifications();
+    }
+
+    @Override
+    public void onNotificationRemoved(String key, StatusBarNotification old) {
+        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
+
+        if (old != null) {
+            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
+                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
+                if (mStatusBarStateController.getState() == StatusBarState.SHADE) {
+                    mCommandQueue.animateCollapsePanels();
+                } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+                        && !isCollapsing()) {
+                    mShadeController.goToKeyguard();
+                }
+            }
+        }
+        mShadeController.updateAreThereNotifications();
+    }
+
+    public boolean hasActiveNotifications() {
+        return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
+    }
+
+    @Override
+    public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+        if (mShadeController.isDozing()) {
+            return false;
+        }
+
+        if (mShadeController.isOccluded()) {
+            boolean devicePublic = mLockscreenUserManager.
+                    isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
+            boolean userPublic = devicePublic
+                    || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
+            boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+            if (userPublic && needsRedaction) {
+                return false;
+            }
+        }
+
+        if (!mCommandQueue.panelsEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
+            }
+            return false;
+        }
+
+        if (sbn.getNotification().fullScreenIntent != null) {
+            if (mAccessibilityManager.isTouchExplorationEnabled()) {
+                if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
+                return false;
+            } else {
+                // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
+                return !mKeyguardMonitor.isShowing()
+                        || mShadeController.isOccluded();
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onUserSwitched(int newUserId) {
+        // Begin old BaseStatusBar.userSwitched
+        mHeadsUpManager.setUser(newUserId);
+        // End old BaseStatusBar.userSwitched
+        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
+        mCommandQueue.animateCollapsePanels();
+        if (mReinflateNotificationsOnUserSwitched) {
+            mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+            mReinflateNotificationsOnUserSwitched = false;
+        }
+        updateNotificationViews();
+        mMediaManager.clearCurrentMediaNotification();
+        mShadeController.setLockscreenUser(newUserId);
+        updateMediaMetaData(true, false);
+    }
+
+    @Override
+    public void onBindRow(Entry entry, PackageManager pmUser,
+            StatusBarNotification sbn, ExpandableNotificationRow row) {
+        row.setAboveShelfChangedListener(mAboveShelfObserver);
+        row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
+    }
+
+    @Override
+    public boolean isPresenterFullyCollapsed() {
+        return mNotificationPanel.isFullyCollapsed();
+    }
+
+    @Override
+    public void onActivated(ActivatableNotificationView view) {
+        onActivated();
+        if (view != null) mNotificationPanel.setActivatedChild(view);
+    }
+
+    public void onActivated() {
+        mLockscreenGestureLogger.write(
+                MetricsEvent.ACTION_LS_NOTE,
+                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+        mNotificationPanel.showTransientIndication(R.string.notification_tap_again);
+        ActivatableNotificationView previousView = mNotificationPanel.getActivatedChild();
+        if (previousView != null) {
+            previousView.makeInactive(true /* animate */);
+        }
+    }
+
+    @Override
+    public void onActivationReset(ActivatableNotificationView view) {
+        if (view == mNotificationPanel.getActivatedChild()) {
+            mNotificationPanel.setActivatedChild(null);
+            mShadeController.onActivationReset();
+        }
+    }
+
+    @Override
+    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
+        mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation);
+    }
+
+    @Override
+    public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
+        RemoteInputController controller = mRemoteInputManager.getController();
+        if (controller.isRemoteInputActive(row.getEntry())
+                && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
+            // We have an active remote input typed and the user clicked on the notification.
+            // this was probably unintentional, so we're closing the edit text instead.
+            controller.closeRemoteInputs();
+            return;
+        }
+        Notification notification = sbn.getNotification();
+        final PendingIntent intent = notification.contentIntent != null
+                ? notification.contentIntent
+                : notification.fullScreenIntent;
+        final String notificationKey = sbn.getKey();
+
+        boolean isActivityIntent = intent.isActivity();
+        final boolean afterKeyguardGone = isActivityIntent
+                && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
+                mLockscreenUserManager.getCurrentUserId());
+        final boolean wasOccluded = mShadeController.isOccluded();
+        boolean showOverLockscreen = mKeyguardMonitor.isShowing()
+                && PreviewInflater.wouldShowOverLockscreen(mContext,
+                intent.getIntent(),
+                mLockscreenUserManager.getCurrentUserId());
+        OnDismissAction postKeyguardAction = () -> {
+            // TODO: Some of this code may be able to move to NotificationEntryManager.
+            if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(notificationKey)) {
+                // Release the HUN notification to the shade.
+
+                if (isPresenterFullyCollapsed()) {
+                    HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+                }
+                //
+                // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+                // become canceled shortly by NoMan, but we can't assume that.
+                mHeadsUpManager.removeNotification(sbn.getKey(),
+                        true /* releaseImmediately */);
+            }
+            StatusBarNotification parentToCancel = null;
+            if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
+                StatusBarNotification summarySbn =
+                        mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+                if (shouldAutoCancel(summarySbn)) {
+                    parentToCancel = summarySbn;
+                }
+            }
+            final StatusBarNotification parentToCancelFinal = parentToCancel;
+            final Runnable runnable = () -> {
+                try {
+                    // The intent we are sending is for the application, which
+                    // won't have permission to immediately start an activity after
+                    // the user switches to home.  We know it is safe to do at this
+                    // point, so make sure new activity switches are now allowed.
+                    ActivityManager.getService().resumeAppSwitches();
+                } catch (RemoteException e) {
+                }
+                int launchResult = ActivityManager.START_CANCELED;
+                if (intent != null) {
+                    // If we are launching a work activity and require to launch
+                    // separate work challenge, we defer the activity action and cancel
+                    // notification until work challenge is unlocked.
+                    if (isActivityIntent) {
+                        final int userId = intent.getCreatorUserHandle().getIdentifier();
+                        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+                                && mKeyguardManager.isDeviceLocked(userId)) {
+                            // TODO(b/28935539): should allow certain activities to
+                            // bypass work challenge
+                            if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
+                                    intent.getIntentSender(), notificationKey)) {
+                                // Show work challenge, do not run PendingIntent and
+                                // remove notification
+                                collapseOnMainThread();
+                                return;
+                            }
+                        }
+                    }
+                    Intent fillInIntent = null;
+                    Entry entry = row.getEntry();
+                    CharSequence remoteInputText = null;
+                    if (!TextUtils.isEmpty(entry.remoteInputText)) {
+                        remoteInputText = entry.remoteInputText;
+                    }
+                    if (!TextUtils.isEmpty(remoteInputText)
+                            && !controller.isSpinning(entry.key)) {
+                        fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
+                                remoteInputText.toString());
+                    }
+                    RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
+                            row, wasOccluded);
+                    try {
+                        if (adapter != null) {
+                            ActivityTaskManager.getService()
+                                    .registerRemoteAnimationForNextActivityStart(
+                                            intent.getCreatorPackage(), adapter);
+                        }
+                        launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
+                                null, null, getActivityOptions(adapter));
+                        mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
+                    } catch (RemoteException | PendingIntent.CanceledException e) {
+                        // the stack trace isn't very helpful here.
+                        // Just log the exception message.
+                        Log.w(TAG, "Sending contentIntent failed: " + e);
+
+                        // TODO: Dismiss Keyguard.
+                    }
+                    if (isActivityIntent) {
+                        mAssistManager.hideAssist();
+                    }
+                }
+                if (shouldCollapse()) {
+                    collapseOnMainThread();
+                }
+
+                final int count =
+                        mEntryManager.getNotificationData().getActiveNotifications().size();
+                final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
+                final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
+                        rank, count, true);
+                try {
+                    mBarService.onNotificationClick(notificationKey, nv);
+                } catch (RemoteException ex) {
+                    // system process is dead if we're here.
+                }
+                if (parentToCancelFinal != null) {
+                    removeNotification(parentToCancelFinal);
+                }
+                if (shouldAutoCancel(sbn)
+                        || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+                                notificationKey)) {
+                    // Automatically remove all notifications that we may have kept around longer
+                    removeNotification(sbn);
+                }
+                mIsCollapsingToShowActivityOverLockscreen = false;
+            };
+
+            if (showOverLockscreen) {
+                mShadeController.addPostCollapseAction(runnable);
+                mShadeController.collapsePanel(true /* animate */);
+            } else if (mKeyguardMonitor.isShowing()
+                    && mShadeController.isOccluded()) {
+                mShadeController.addAfterKeyguardGoneRunnable(runnable);
+                mShadeController.collapsePanel();
+            } else {
+                new Thread(runnable).start();
+            }
+
+            return !mNotificationPanel.isFullyCollapsed();
+        };
+        if (showOverLockscreen) {
+            mIsCollapsingToShowActivityOverLockscreen = true;
+            postKeyguardAction.onDismiss();
+        } else {
+            mActivityStarter.dismissKeyguardThenExecute(
+                    postKeyguardAction, null /* cancel */, afterKeyguardGone);
+        }
+    }
+
+    private void removeNotification(StatusBarNotification notification) {
+        // We have to post it to the UI thread for synchronization
+        Dependency.get(MAIN_HANDLER).post(() -> {
+            Runnable removeRunnable =
+                    () -> mEntryManager.performRemoveNotification(notification);
+            if (isCollapsing()) {
+                // To avoid lags we're only performing the remove
+                // after the shade was collapsed
+                mShadeController.addPostCollapseAction(removeRunnable);
+            } else {
+                removeRunnable.run();
+            }
+        });
+    }
+
+    @Override
+    public void startNotificationGutsIntent(final Intent intent, final int appUid,
+            ExpandableNotificationRow row) {
+        mActivityStarter.dismissKeyguardThenExecute(() -> {
+            AsyncTask.execute(() -> {
+                int launchResult = TaskStackBuilder.create(mContext)
+                        .addNextIntentWithParentStack(intent)
+                        .startActivities(getActivityOptions(
+                                mActivityLaunchAnimator.getLaunchAnimation(
+                                        row, mShadeController.isOccluded())),
+                                new UserHandle(UserHandle.getUserId(appUid)));
+                mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
+                if (shouldCollapse()) {
+                    // Putting it back on the main thread, since we're touching views
+                    Dependency.get(MAIN_HANDLER).post(() -> mCommandQueue.animateCollapsePanels(
+                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
+                }
+            });
+            return true;
+        }, null, false /* afterKeyguardGone */);
+    }
+
+    @Override
+    public int getMaxNotificationsWhileLocked(boolean recompute) {
+        if (recompute) {
+            mMaxKeyguardNotifications = Math.max(1,
+                    mNotificationPanel.computeMaxKeyguardNotifications(
+                            mMaxAllowedKeyguardNotifications));
+            return mMaxKeyguardNotifications;
+        }
+        return mMaxKeyguardNotifications;
+    }
+
+    @Override
+    public void onUpdateRowStates() {
+        mNotificationPanel.onUpdateRowStates();
+    }
+
+    @Override
+    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
+        mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
+        if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
+            mShadeController.goToLockedShade(clickedEntry.row);
+        }
+    }
+
+    @Override
+    public boolean isDeviceInVrMode() {
+        return mVrMode;
+    }
+
+    @Override
+    public boolean isPresenterLocked() {
+        return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+    }
+
+    private void collapseOnMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            mShadeController.collapsePanel();
+        } else {
+            Dependency.get(MAIN_HANDLER).post(mShadeController::collapsePanel);
+        }
+    }
+
+    private boolean shouldCollapse() {
+        return mStatusBarStateController.getState() != StatusBarState.SHADE
+                || !mActivityLaunchAnimator.isAnimationPending();
+    }
+
+    private void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
+        mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+        mActivityStarter.dismissKeyguardThenExecute(dismissAction, null,
+                true /* afterKeyguardGone */);
+    }
+
+    private static boolean shouldAutoCancel(StatusBarNotification sbn) {
+        int flags = sbn.getNotification().flags;
+        if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
+            return false;
+        }
+        if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return false;
+        }
+        return true;
+    }
+
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            mVrMode = enabled;
+        }
+    };
+
+    private final CheckSaveListener mCheckSaveListener = new CheckSaveListener() {
+        @Override
+        public void checkSave(Runnable saveImportance, StatusBarNotification sbn) {
+            int state = mStatusBarStateController.getState();
+            // If the user has security enabled, show challenge if the setting is changed.
+            if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
+                    && mKeyguardManager.isKeyguardLocked()) {
+                onLockedNotificationImportanceChange(() -> {
+                    saveImportance.run();
+                    return true;
+                });
+            } else {
+                saveImportance.run();
+            }
+        }
+    };
+
+    private final OnSettingsClickListener mOnSettingsClickListener = new OnSettingsClickListener() {
+        @Override
+        public void onSettingsClick(String key) {
+            try {
+                mBarService.onNotificationSettingsViewed(key);
+            } catch (RemoteException e) {
+                // if we're here we're dead
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
new file mode 100644
index 0000000..06f9658
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
+
+import static com.android.systemui.SysUiServiceProvider.getComponent;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager
+        .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
+
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.PreviewInflater;
+
+public class StatusBarRemoteInputCallback implements Callback, Callbacks {
+
+    private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
+    private final StatusBarStateController mStatusBarStateController
+            = Dependency.get(StatusBarStateController.class);
+    private final NotificationLockscreenUserManager mLockscreenUserManager
+            = Dependency.get(NotificationLockscreenUserManager.class);
+    private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+    private final Context mContext;
+    private View mPendingWorkRemoteInputView;
+    private final StatusBarStateController.StateListener mStateListener = this::setStatusBarState;
+    private View mPendingRemoteInputView;
+    private final ShadeController mShadeController = Dependency.get(ShadeController.class);
+    private KeyguardManager mKeyguardManager;
+    private final CommandQueue mCommandQueue;
+    private int mDisabled2;
+    protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
+
+    public StatusBarRemoteInputCallback(Context context) {
+        mContext = context;
+        mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
+                new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
+        mStatusBarStateController.addListener(mStateListener);
+        mKeyguardManager = context.getSystemService(KeyguardManager.class);
+        mCommandQueue = getComponent(context, CommandQueue.class);
+        mCommandQueue.addCallbacks(this);
+    }
+
+    private void setStatusBarState(int state) {
+        if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) {
+            if (!mStatusBarStateController.isKeyguardRequested()) {
+                if (mPendingRemoteInputView != null
+                        && mPendingRemoteInputView.isAttachedToWindow()) {
+                    mPendingRemoteInputView.post(mPendingRemoteInputView::callOnClick);
+                }
+                mPendingRemoteInputView = null;
+            }
+        }
+    }
+
+    @Override
+    public void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
+        mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+        mShadeController.showBouncer(true /* scrimmed */);
+        mPendingRemoteInputView = clicked;
+    }
+
+    protected void onWorkChallengeChanged() {
+        if (mPendingWorkRemoteInputView != null
+                && !mLockscreenUserManager.isAnyProfilePublicMode()) {
+            // Expand notification panel and the notification row, then click on remote input view
+            final Runnable clickPendingViewRunnable = () -> {
+                final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+                if (pendingWorkRemoteInputView == null) {
+                    return;
+                }
+
+                // Climb up the hierarchy until we get to the container for this row.
+                ViewParent p = pendingWorkRemoteInputView.getParent();
+                while (!(p instanceof ExpandableNotificationRow)) {
+                    if (p == null) {
+                        return;
+                    }
+                    p = p.getParent();
+                }
+
+                final ExpandableNotificationRow row = (ExpandableNotificationRow) p;
+                ViewParent viewParent = row.getParent();
+                if (viewParent instanceof NotificationStackScrollLayout) {
+                    final NotificationStackScrollLayout scrollLayout =
+                            (NotificationStackScrollLayout) viewParent;
+                    row.makeActionsVisibile();
+                    row.post(() -> {
+                        final Runnable finishScrollingCallback = () -> {
+                            mPendingWorkRemoteInputView.callOnClick();
+                            mPendingWorkRemoteInputView = null;
+                            scrollLayout.setFinishScrollingCallback(null);
+                        };
+                        if (scrollLayout.scrollTo(row)) {
+                            // It scrolls! So call it when it's finished.
+                            scrollLayout.setFinishScrollingCallback(finishScrollingCallback);
+                        } else {
+                            // It does not scroll, so call it now!
+                            finishScrollingCallback.run();
+                        }
+                    });
+                }
+            };
+            mShadeController.postOnShadeExpanded(clickPendingViewRunnable);
+            mShadeController.instantExpandNotificationsPanel();
+        }
+    }
+
+    @Override
+    public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
+            View clickedView) {
+        if (mKeyguardMonitor.isShowing()) {
+            onLockedRemoteInput(row, clickedView);
+        } else {
+            row.setUserExpanded(true);
+            row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
+        }
+    }
+
+    @Override
+    public void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
+            View clicked) {
+        // Collapse notification and show work challenge
+        mCommandQueue.animateCollapsePanels();
+        startWorkChallengeIfNecessary(userId, null, null);
+        // Add pending remote input view after starting work challenge, as starting work challenge
+        // will clear all previous pending review view
+        mPendingWorkRemoteInputView = clicked;
+    }
+
+    protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
+            String notificationKey) {
+        // Clear pending remote view, as we do not want to trigger pending remote input view when
+        // it's called by other code
+        mPendingWorkRemoteInputView = null;
+        // Begin old BaseStatusBar.startWorkChallengeIfNecessary.
+        final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
+                null, userId);
+        if (newIntent == null) {
+            return false;
+        }
+        final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
+        callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
+        callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
+        callBackIntent.setPackage(mContext.getPackageName());
+
+        PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
+                mContext,
+                0,
+                callBackIntent,
+                PendingIntent.FLAG_CANCEL_CURRENT |
+                        PendingIntent.FLAG_ONE_SHOT |
+                        PendingIntent.FLAG_IMMUTABLE);
+        newIntent.putExtra(
+                Intent.EXTRA_INTENT,
+                callBackPendingIntent.getIntentSender());
+        try {
+            ActivityManager.getService().startConfirmDeviceCredentialIntent(newIntent,
+                    null /*options*/);
+        } catch (RemoteException ex) {
+            // ignore
+        }
+        return true;
+        // End old BaseStatusBar.startWorkChallengeIfNecessary.
+    }
+
+    @Override
+    public boolean shouldHandleRemoteInput(View view, PendingIntent pendingIntent) {
+        // Skip remote input as doing so will expand the notification shade.
+        return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
+    }
+
+    @Override
+    public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+            Intent fillInIntent, NotificationRemoteInputManager.ClickHandler defaultHandler) {
+        final boolean isActivity = pendingIntent.isActivity();
+        if (isActivity) {
+            final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
+                    mContext, pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
+            mActivityStarter.dismissKeyguardThenExecute(() -> {
+                try {
+                    ActivityManager.getService().resumeAppSwitches();
+                } catch (RemoteException e) {
+                }
+
+                boolean handled = defaultHandler.handleClick();
+
+                // close the shade if it was open and maybe wait for activity start.
+                return handled && mShadeController.closeShadeIfOpen();
+            }, null, afterKeyguardGone);
+            return true;
+        } else {
+            return defaultHandler.handleClick();
+        }
+    }
+
+    @Override
+    public void disable(int state1, int state2, boolean animate) {
+        mDisabled2 = state2;
+    }
+
+    protected class ChallengeReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
+                if (userId != mLockscreenUserManager.getCurrentUserId()
+                        && mLockscreenUserManager.isCurrentProfile(userId)) {
+                    onWorkChallengeChanged();
+                }
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 45b32c7..ad9b9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -58,6 +58,7 @@
 import com.android.internal.view.FloatingActionMode;
 import com.android.internal.widget.FloatingToolbar;
 import com.android.systemui.Dependency;
+import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.DragDownHelper;
@@ -182,6 +183,11 @@
         }
     }
 
+    @VisibleForTesting
+    protected NotificationStackScrollLayout getStackScrollLayout() {
+        return mStackScrollLayout;
+    }
+
     @Override
     public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(getContext(), attrs);
@@ -215,8 +221,11 @@
 
     public void setService(StatusBar service) {
         mService = service;
-        setDragDownHelper(new DragDownHelper(getContext(), this, mStackScrollLayout,
-                mStackScrollLayout));
+        NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+        ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback();
+        DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback();
+        setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback,
+                dragDownCallback));
     }
 
     @VisibleForTesting
@@ -309,7 +318,7 @@
             }
         }
         if (isDown) {
-            mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+            getStackScrollLayout().closeControlsIfOutsideTouch(ev);
         }
         if (mService.isDozing()) {
             mService.mDozeScrimController.extendPulse();
@@ -331,13 +340,14 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mService.isDozing() && !mStackScrollLayout.hasPulsingNotifications()) {
+        NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+        if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) {
             // Capture all touch events in always-on.
             return true;
         }
         boolean intercept = false;
         if (mNotificationPanel.isFullyExpanded()
-                && mStackScrollLayout.getVisibility() == View.VISIBLE
+                && stackScrollLayout.getVisibility() == View.VISIBLE
                 && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
                 && !mService.isBouncerShowing()
                 && !mService.isDozing()) {
@@ -349,7 +359,7 @@
         if (intercept) {
             MotionEvent cancellation = MotionEvent.obtain(ev);
             cancellation.setAction(MotionEvent.ACTION_CANCEL);
-            mStackScrollLayout.onInterceptTouchEvent(cancellation);
+            stackScrollLayout.onInterceptTouchEvent(cancellation);
             mNotificationPanel.onInterceptTouchEvent(cancellation);
             cancellation.recycle();
         }
@@ -391,8 +401,9 @@
     }
 
     public void cancelExpandHelper() {
-        if (mStackScrollLayout != null) {
-            mStackScrollLayout.cancelExpandHelper();
+        NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
+        if (stackScrollLayout != null) {
+            stackScrollLayout.cancelExpandHelper();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index d477587..b4d24d16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -151,6 +153,7 @@
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
+        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
     }
 
     protected void updatePinnedMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 8e32a0b..59bd85e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -27,8 +27,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.os.SystemProperties;
-import android.view.DisplayListCanvas;
+import android.graphics.RecordingCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -122,7 +121,7 @@
     public void draw(Canvas canvas) {
         mSupportHardware = canvas.isHardwareAccelerated();
         if (mSupportHardware) {
-            drawHardware((DisplayListCanvas) canvas);
+            drawHardware((RecordingCanvas) canvas);
         } else {
             drawSoftware(canvas);
         }
@@ -147,7 +146,7 @@
         return getBounds().width() > getBounds().height();
     }
 
-    private void drawHardware(DisplayListCanvas c) {
+    private void drawHardware(RecordingCanvas c) {
         if (mDrawingHardwareGlow) {
             c.drawRoundRect(mLeftProp, mTopProp, mRightProp, mBottomProp, mRxProp, mRyProp,
                     mPaintProp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 7b42dd4..aba2377 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -24,6 +24,7 @@
     boolean isOccluded();
     boolean isKeyguardFadingAway();
     boolean isKeyguardGoingAway();
+    boolean isLaunchTransitionFadingAway();
     long getKeyguardFadingAwayDuration();
     long getKeyguardFadingAwayDelay();
     long calculateGoingToFullShadeDelay();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 10cb09b..5eb0fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -28,7 +28,7 @@
 public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback
         implements KeyguardMonitor {
 
-    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
 
     private final Context mContext;
     private final CurrentUserTracker mUserTracker;
@@ -45,6 +45,7 @@
     private long mKeyguardFadingAwayDelay;
     private long mKeyguardFadingAwayDuration;
     private boolean mKeyguardGoingAway;
+    private boolean mLaunchTransitionFadingAway;
 
     public KeyguardMonitorImpl(Context context) {
         mContext = context;
@@ -123,7 +124,7 @@
 
     private void notifyKeyguardChanged() {
         // Copy the list to allow removal during callback.
-        new ArrayList<Callback>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
+        new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
     }
 
     public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) {
@@ -165,4 +166,13 @@
     public void notifyKeyguardGoingAway(boolean keyguardGoingAway) {
         mKeyguardGoingAway = keyguardGoingAway;
     }
+
+    public void setLaunchTransitionFadingAway(boolean fadingAway) {
+        mLaunchTransitionFadingAway = fadingAway;
+    }
+
+    @Override
+    public boolean isLaunchTransitionFadingAway() {
+        return mLaunchTransitionFadingAway;
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 24a28cb..70a3589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -461,6 +461,8 @@
                     MobileSignalController controller = mMobileSignalControllers.valueAt(i);
                     controller.handleBroadcast(intent);
                 }
+                mConfig = Config.readConfig(mContext);
+                mReceiverHandler.post(this::handleConfigurationChanged);
                 break;
             case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
                 // Avoid rebroadcast because SysUI is direct boot aware.
@@ -1042,18 +1044,23 @@
             config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
             config.alwaysShowCdmaRssi =
                     res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
-            config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
             config.hspaDataDistinguishable =
                     res.getBoolean(R.bool.config_hspa_data_distinguishable);
-            config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
             config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
 
             CarrierConfigManager configMgr = (CarrierConfigManager)
                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = configMgr.getConfig();
+            // Handle specific carrier config values for the default data SIM
+            int defaultDataSubId = SubscriptionManager.from(context)
+                    .getDefaultDataSubscriptionId();
+            PersistableBundle b = configMgr.getConfigForSubId(defaultDataSubId);
             if (b != null) {
                 config.alwaysShowDataRatIcon = b.getBoolean(
                         CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
+                config.show4gForLte = b.getBoolean(
+                        CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
+                config.hideLtePlus = b.getBoolean(
+                        CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
             }
             return config;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index dd03162..aa4782f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -28,7 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ContrastColorUtil;
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationData;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a97effd..e20e267 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -956,11 +956,13 @@
                 changed |= onVolumeChangedW(stream, 0);
             } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
                 final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+                if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm;
                 if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
                         + Util.ringerModeToString(rm));
                 changed = updateRingerModeExternalW(rm);
             } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
                 final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+                if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm;
                 if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
                         + Util.ringerModeToString(rm));
                 changed = updateRingerModeInternalW(rm);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 4810b0b..798f8bc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,10 +135,6 @@
     private final AccessibilityManagerWrapper mAccessibilityMgr;
     private final Object mSafetyWarningLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
-    private ColorStateList mActiveTint;
-    private int mActiveAlpha;
-    private ColorStateList mInactiveTint;
-    private int mInactiveAlpha;
 
     private boolean mShowing;
     private boolean mShowA11yStream;
@@ -238,11 +234,6 @@
         lp.gravity = ((FrameLayout.LayoutParams) mDialogView.getLayoutParams()).gravity;
         mWindow.setAttributes(lp);
 
-        mActiveTint = Utils.getColorAccent(mContext);
-        mActiveAlpha = Color.alpha(mActiveTint.getDefaultColor());
-        mInactiveTint = Utils.getColorAttr(mContext, android.R.attr.colorForeground);
-        mInactiveAlpha = getAlphaAttr(android.R.attr.secondaryContentAlpha);
-
         mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
         mRinger = mDialog.findViewById(R.id.ringer);
         if (mRinger != null) {
@@ -556,14 +547,15 @@
         mHandler.removeMessages(H.SHOW);
         mHandler.removeMessages(H.DISMISS);
         rescheduleTimeoutH();
-        mShowing = true;
 
         if (mConfigChanged) {
-            initDialog();
+            initDialog(); // resets mShowing to false
             mConfigurableTexts.update();
             mConfigChanged = false;
         }
+
         initSettingsH();
+        mShowing = true;
         mDialog.show();
         Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
         mController.notifyVisible(true);
@@ -941,8 +933,12 @@
             row.slider.requestFocus();
         }
         boolean useActiveColoring = isActive && row.slider.isEnabled();
-        final ColorStateList tint = useActiveColoring ? mActiveTint : mInactiveTint;
-        final int alpha = useActiveColoring ? mActiveAlpha : mInactiveAlpha;
+        final ColorStateList tint = useActiveColoring
+                ? Utils.getColorAccent(mContext)
+                : Utils.getColorAttr(mContext, android.R.attr.colorForeground);
+        final int alpha = useActiveColoring
+                ? Color.alpha(tint.getDefaultColor())
+                : getAlphaAttr(android.R.attr.secondaryContentAlpha);
         if (tint == row.cachedTint) return;
         row.slider.setProgressTintList(tint);
         row.slider.setThumbTintList(tint);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
index 3f85c9d..a69fd56 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewTest.java
@@ -23,6 +23,7 @@
 import android.testing.TestableLooper;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -44,7 +45,7 @@
     @Test
     public void testHasDismissActions() {
         Assert.assertFalse("Action not set yet", mKeyguardHostView.hasDismissActions());
-        mKeyguardHostView.setOnDismissAction(mock(KeyguardHostView.OnDismissAction.class),
+        mKeyguardHostView.setOnDismissAction(mock(OnDismissAction.class),
                 null /* cancelAction */);
         Assert.assertTrue("Action should exist", mKeyguardHostView.hasDismissActions());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index a35ca46..a58bc85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -58,7 +58,6 @@
 
     @Before
     public void SysuiSetup() throws Exception {
-        mContext.setTheme(R.style.Theme_SystemUI);
         SystemUIFactory.createFromConfig(mContext);
 
         mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
index 9d3124e..e811270 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -25,10 +25,12 @@
 
     public SysuiTestableContext(Context base) {
         super(base);
+        setTheme(R.style.Theme_SystemUI);
     }
 
     public SysuiTestableContext(Context base, LeakCheck check) {
         super(base, check);
+        setTheme(R.style.Theme_SystemUI);
     }
 
     public ArrayMap<Class<?>, Object> getComponents() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index 5c83d99..0c8d137 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -25,6 +25,9 @@
     private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
 
     public TestableDependency(Context context) {
+        if (context instanceof SysuiTestableContext) {
+            mComponents = ((SysuiTestableContext) context).getComponents();
+        }
         mContext = context;
         if (SystemUIFactory.getInstance() == null) {
             SystemUIFactory.createFromConfig(context);
@@ -43,6 +46,9 @@
     }
 
     public <T> void injectTestDependency(Class<T> key, T obj) {
+        if (mInstantiatedObjects.contains(key)) {
+            throw new IllegalStateException(key + " was already initialized");
+        }
         mObjs.put(key, obj);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 2398fd3..6abd407 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -115,6 +115,11 @@
     }
 
     @Override
+    public void setPassiveInterrupt(boolean lightInterrupt) {
+
+    }
+
+    @Override
     public void setDozeScreenBrightness(int value) {
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index a02ef98..8ae3cd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -84,7 +84,7 @@
     public void testCollapsePanels() {
         mCommandQueue.animateCollapsePanels();
         waitForIdleSync();
-        verify(mCallbacks).animateCollapsePanels(eq(0));
+        verify(mCallbacks).animateCollapsePanels(eq(0), eq(false));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index bdd05c7..aae6d93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -28,6 +30,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
 import android.content.Context;
+import android.graphics.Color;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Looper;
 import android.support.test.InstrumentationRegistry;
@@ -45,6 +48,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -54,9 +59,13 @@
 
     private String mDisclosureWithOrganization;
 
-    private DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
-    private ViewGroup mIndicationArea = mock(ViewGroup.class);
-    private KeyguardIndicationTextView mDisclosure = mock(KeyguardIndicationTextView.class);
+    @Mock
+    private DevicePolicyManager mDevicePolicyManager;
+    @Mock
+    private ViewGroup mIndicationArea;
+    @Mock
+    private KeyguardIndicationTextView mDisclosure;
+    private KeyguardIndicationTextView mTextView;
 
     private KeyguardIndicationController mController;
     private WakeLockFake mWakeLock;
@@ -64,7 +73,9 @@
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mTextView = new KeyguardIndicationTextView(mContext);
 
         mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
         mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
@@ -74,6 +85,7 @@
 
         when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure))
                 .thenReturn(mDisclosure);
+        when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
 
         mWakeLock = new WakeLockFake();
     }
@@ -189,4 +201,17 @@
         });
         assertFalse("WakeLock expected: RELEASED, was: HELD", held[0]);
     }
+
+    @Test
+    public void transientIndication_visibleWhenDozing() {
+        createController();
+
+        mController.setVisible(true);
+        mController.showTransientIndication("Test");
+        mController.setDozing(true);
+
+        assertThat(mTextView.getText()).isEqualTo("Test");
+        assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
+        assertThat(mTextView.getAlpha()).isEqualTo(1f);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index da59450..2e280d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -17,26 +17,27 @@
 package com.android.systemui.statusbar;
 
 import static org.junit.Assert.assertFalse;
-import static org.mockito.Mockito.when;
 
 import android.os.Handler;
-import android.os.Looper;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInfo;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
+import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -49,25 +50,29 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@Ignore("b/118400112")
 public class NonPhoneDependencyTest extends SysuiTestCase {
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationListContainer mListContainer;
     @Mock private NotificationEntryManager.Callback mEntryManagerCallback;
     @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private RemoteInputController.Delegate mDelegate;
-    @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
-    @Mock private NotificationGutsManager.OnSettingsClickListener mOnClickListener;
     @Mock private NotificationRemoteInputManager.Callback mRemoteInputManagerCallback;
+    @Mock private CheckSaveListener mCheckSaveListener;
+    @Mock private OnSettingsClickListener mOnSettingsClickListener;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+               new Handler(TestableLooper.get(this).getLooper()));
     }
 
     @Test
+    @Ignore("b/118400112")
     public void testNotificationManagementCodeHasNoDependencyOnStatusBarWindowManager() {
+        mDependency.injectMockDependency(ShadeController.class);
         NotificationEntryManager entryManager = Dependency.get(NotificationEntryManager.class);
         NotificationGutsManager gutsManager = Dependency.get(NotificationGutsManager.class);
         NotificationListener notificationListener = Dependency.get(NotificationListener.class);
@@ -79,24 +84,20 @@
                 Dependency.get(NotificationLockscreenUserManager.class);
         NotificationViewHierarchyManager viewHierarchyManager =
                 Dependency.get(NotificationViewHierarchyManager.class);
-        NotificationGroupManager groupManager = Dependency.get(NotificationGroupManager.class);
-
-        when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(lockscreenUserManager);
-        when(mPresenter.getGroupManager()).thenReturn(groupManager);
-
+        Dependency.get(InitController.class).executePostInitTasks();
         entryManager.setUpWithPresenter(mPresenter, mListContainer, mEntryManagerCallback,
                 mHeadsUpManager);
-        groupManager.setHeadsUpManager(mHeadsUpManager);
-        gutsManager.setUpWithPresenter(mPresenter, mListContainer, mCheckSaveListener,
-                mOnClickListener);
-        notificationLogger.setUpWithEntryManager(entryManager, mListContainer);
-        mediaManager.setUpWithPresenter(mPresenter, entryManager);
-        remoteInputManager.setUpWithPresenter(mPresenter, entryManager, mRemoteInputManagerCallback,
+        gutsManager.setUpWithPresenter(mPresenter, mListContainer,
+                mCheckSaveListener, mOnSettingsClickListener);
+        notificationLogger.setUpWithContainer(mListContainer);
+        mediaManager.setUpWithPresenter(mPresenter);
+        remoteInputManager.setUpWithPresenter(mPresenter, mRemoteInputManagerCallback,
                 mDelegate);
-        lockscreenUserManager.setUpWithPresenter(mPresenter, entryManager);
-        viewHierarchyManager.setUpWithPresenter(mPresenter, entryManager, mListContainer);
-        notificationListener.setUpWithPresenter(mPresenter, entryManager);
+        lockscreenUserManager.setUpWithPresenter(mPresenter);
+        viewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
+        notificationListener.setUpWithPresenter(mPresenter);
 
+        TestableLooper.get(this).processAllMessages();
         assertFalse(mDependency.hasInstantiatedDependency(StatusBarWindowController.class));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 7b0c0a0..63ececb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -32,6 +32,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -44,7 +45,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class NotificationListenerTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
@@ -66,15 +67,16 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectTestDependency(NotificationRemoteInputManager.class,
                 mRemoteInputManager);
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+                new Handler(TestableLooper.get(this).getLooper()));
 
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
         mListener = new NotificationListener(mContext);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
                 new Notification(), UserHandle.CURRENT, null, 0);
 
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+        mListener.setUpWithPresenter(mPresenter);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 515c109..f168a49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -40,7 +40,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
-import android.util.Log;
+import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -83,12 +83,12 @@
 
         when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
                 new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+                Handler.createAsync(Looper.myLooper()));
 
         mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
-        mLockscreenUserManager.setUpWithPresenter(mPresenter, mEntryManager);
-        mLockscreenUserManager.setKeyguardViewManager(mKeyguardViewManager);
+        mLockscreenUserManager.setUpWithPresenter(mPresenter);
     }
 
     @Test
@@ -137,15 +137,6 @@
     }
 
     @Test
-    public void testActionDeviceLockedChangedWithDifferentUserIdCallsOnWorkChallengeChanged() {
-        Intent intent = new Intent()
-                .setAction(ACTION_DEVICE_LOCKED_CHANGED)
-                .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1);
-        mLockscreenUserManager.getAllUsersReceiverForTest().onReceive(mContext, intent);
-        verify(mPresenter, times(1)).onWorkChallengeChanged();
-    }
-
-    @Test
     public void testActionUserSwitchedCallsOnUserSwitched() {
         Intent intent = new Intent()
                 .setAction(ACTION_USER_SWITCHED)
@@ -161,15 +152,11 @@
         assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId));
     }
 
-    private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManager {
+    private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManagerImpl {
         public TestNotificationLockscreenUserManager(Context context) {
             super(context);
         }
 
-        public BroadcastReceiver getAllUsersReceiverForTest() {
-            return mAllUsersReceiver;
-        }
-
         public BroadcastReceiver getBaseBroadcastReceiverForTest() {
             return mBaseBroadcastReceiver;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 8a59e96..d409e2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -20,6 +20,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -70,8 +71,8 @@
         mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
                 mLockscreenUserManager);
         mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
-
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+                Handler.createAsync(Looper.myLooper()));
 
         mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
@@ -79,7 +80,7 @@
         mEntry = new NotificationData.Entry(mSbn);
         mEntry.row = mRow;
 
-        mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mEntryManager, mCallback,
+        mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
                 mDelegate, mController);
         for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) {
             extender.setCallback(
@@ -201,11 +202,10 @@
         }
 
         public void setUpWithPresenterForTest(NotificationPresenter presenter,
-                NotificationEntryManager entryManager,
                 Callback callback,
                 RemoteInputController.Delegate delegate,
                 RemoteInputController controller) {
-            super.setUpWithPresenter(presenter, entryManager, callback, delegate);
+            super.setUpWithPresenter(presenter, callback, delegate);
             mRemoteInputController = controller;
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index edf29ac..aca1f90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,9 +31,9 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -67,16 +67,50 @@
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
     }
 
+    /**
+     * Creates a generic row.
+     *
+     * @return a generic row with no special properties.
+     * @throws Exception
+     */
     public ExpandableNotificationRow createRow() throws Exception {
         return createRow(PKG, UID);
     }
 
+    /**
+     * Create a row with the package and user id specified.
+     *
+     * @param pkg package
+     * @param uid user id
+     * @return a row with a notification using the package and user id
+     * @throws Exception
+     */
     public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
         return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
     }
 
+    /**
+     * Creates a row based off the notification given.
+     *
+     * @param notification the notification
+     * @return a row built off the notification
+     * @throws Exception
+     */
     public ExpandableNotificationRow createRow(Notification notification) throws Exception {
-        return generateRow(notification, PKG, UID, false /* isGroupRow */);
+        return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
+    }
+
+    /**
+     * Create a row with the specified content views inflated in addition to the default.
+     *
+     * @param extraInflationFlags the flags corresponding to the additional content views that
+     *                            should be inflated
+     * @return a row with the specified content views inflated in addition to the default
+     * @throws Exception
+     */
+    public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
+            throws Exception {
+        return generateRow(createNotification(), PKG, UID, extraInflationFlags);
     }
 
     /**
@@ -122,34 +156,53 @@
             boolean isGroupSummary,
             @Nullable String groupKey)
             throws Exception {
+        Notification notif = createNotification(isGroupSummary, groupKey);
+        return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
+    }
+
+    /**
+     * Creates a generic notification.
+     *
+     * @return a notification with no special properties
+     */
+    private Notification createNotification() {
+        return createNotification(false /* isGroupSummary */, null /* groupKey */);
+    }
+
+    /**
+     * Creates a notification with the given parameters.
+     *
+     * @param isGroupSummary whether the notification is a group summary
+     * @param groupKey the group key for the notification group used across notifications
+     * @return a notification that is in the group specified or standalone if unspecified
+     */
+    private Notification createNotification(boolean isGroupSummary,
+            @Nullable String groupKey) {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
                         R.layout.custom_view_dark))
                 .build();
-        Notification.Builder notificationBuilder =
-                new Notification.Builder(mContext, "channelId")
-                        .setSmallIcon(R.drawable.ic_person)
-                        .setContentTitle("Title")
-                        .setContentText("Text")
-                        .setPublicVersion(publicVersion);
-
-        // Group notification setup
+        Notification.Builder notificationBuilder = new Notification.Builder(mContext, "channelId")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text")
+                .setPublicVersion(publicVersion)
+                .setStyle(new Notification.BigTextStyle().bigText("Big Text"));
         if (isGroupSummary) {
             notificationBuilder.setGroupSummary(true);
         }
         if (!TextUtils.isEmpty(groupKey)) {
             notificationBuilder.setGroup(groupKey);
         }
-
-        return generateRow(notificationBuilder.build(), pkg, uid, !TextUtils.isEmpty(groupKey));
+        return notificationBuilder.build();
     }
 
     private ExpandableNotificationRow generateRow(
             Notification notification,
             String pkg,
             int uid,
-            boolean isGroupRow)
+            @InflationFlag int extraInflationFlags)
             throws Exception {
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
@@ -179,8 +232,10 @@
         entry.channel = new NotificationChannel(
                 notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
         entry.channel.setBlockableSystem(true);
+        row.setEntry(entry);
+        row.getNotificationInflater().addInflationFlags(extraInflationFlags);
         NotificationInflaterTest.runThenWaitForInflation(
-                () -> row.updateNotification(entry),
+                () -> row.inflateViews(),
                 row.getNotificationInflater());
 
         // This would be done as part of onAsyncInflationFinished, but we skip large amounts of
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 15c18e9..602e613 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -32,6 +32,8 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import com.android.systemui.Dependency;
+import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
@@ -42,6 +44,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import com.google.android.collect.Lists;
 
@@ -67,6 +70,7 @@
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private NotificationGroupManager mGroupManager;
     @Mock private VisualStabilityManager mVisualStabilityManager;
+    @Mock private ShadeController mShadeController;
 
     private NotificationViewHierarchyManager mViewHierarchyManager;
     private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);;
@@ -79,11 +83,13 @@
                 mLockscreenUserManager);
         mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
+        mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
         when(mEntryManager.getNotificationData()).thenReturn(mNotificationData);
 
         mViewHierarchyManager = new NotificationViewHierarchyManager(mContext);
-        mViewHierarchyManager.setUpWithPresenter(mPresenter, mEntryManager, mListContainer);
+        Dependency.get(InitController.class).executePostInitTasks();
+        mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
     }
 
     private NotificationData.Entry createEntry() throws Exception {
@@ -218,6 +224,9 @@
         public void generateChildOrderChangedEvent() {}
 
         @Override
+        public void onReset(ExpandableView view) {}
+
+        @Override
         public int getContainerChildCount() {
             return mRows.size();
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 17daaac..1d977d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -80,8 +80,7 @@
                 mSmartReplyController);
 
         mRemoteInputManager = new NotificationRemoteInputManager(mContext);
-        mRemoteInputManager.setUpWithPresenter(mPresenter, mNotificationEntryManager, mCallback,
-                mDelegate);
+        mRemoteInputManager.setUpWithPresenter(mPresenter, mCallback, mDelegate);
         mNotification = new Notification.Builder(mContext, "")
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
index 78be783..b405a5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AppOpsListenerTest.java
@@ -18,7 +18,6 @@
 
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
 import android.os.Handler;
@@ -27,6 +26,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -59,14 +59,15 @@
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
         getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+                Handler.createAsync(Looper.myLooper()));
 
         mListener = new AppOpsListener(mContext);
     }
 
     @Test
     public void testOnlyListenForFewOps() {
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+        mListener.setUpWithPresenter(mPresenter);
 
         verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsListener.OPS, mListener);
     }
@@ -79,7 +80,7 @@
 
     @Test
     public void testInformEntryMgrOnAppOpsChange() {
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+        mListener.setUpWithPresenter(mPresenter);
         mListener.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         TestableLooper.get(this).processAllMessages();
@@ -89,7 +90,7 @@
 
     @Test
     public void testInformFscOnAppOpsChange() {
-        mListener.setUpWithPresenter(mPresenter, mEntryManager);
+        mListener.setUpWithPresenter(mPresenter);
         mListener.onOpActiveChanged(
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true);
         TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index de5a8a0..459dc5d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -55,13 +55,16 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.ArraySet;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -87,7 +90,7 @@
     @Mock
     ForegroundServiceController mFsc;
     @Mock
-    NotificationData.Environment mEnvironment;
+    NotificationData.KeyguardEnvironment mEnvironment;
 
     private final IPackageManager mMockPackageManager = mock(IPackageManager.class);
     private NotificationData mNotificationData;
@@ -108,10 +111,14 @@
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
 
         mDependency.injectTestDependency(ForegroundServiceController.class, mFsc);
-        when(mEnvironment.getGroupManager()).thenReturn(new NotificationGroupManager());
+        mDependency.injectTestDependency(NotificationGroupManager.class,
+                new NotificationGroupManager());
+        mDependency.injectMockDependency(ShadeController.class);
+        mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
-        mNotificationData = new TestableNotificationData(mEnvironment);
+        mNotificationData = new TestableNotificationData();
+        Dependency.get(InitController.class).executePostInitTasks();
         mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
         mRow = new NotificationTestHelper(getContext()).createRow();
     }
@@ -298,11 +305,11 @@
                 mRow.getEntry().notification)).thenReturn(false);
         when(mEnvironment.isNotificationForCurrentProfiles(
                 row2.getEntry().notification)).thenReturn(true);
-        ArrayList<NotificationData.Entry> reuslt =
+        ArrayList<NotificationData.Entry> result =
                 mNotificationData.getNotificationsForCurrentUser();
 
-        assertEquals(reuslt.size(), 1);
-        junit.framework.Assert.assertEquals(reuslt.get(0), row2.getEntry());
+        assertEquals(result.size(), 1);
+        junit.framework.Assert.assertEquals(result.get(0), row2.getEntry());
     }
 
     @Test
@@ -416,8 +423,8 @@
     }
 
     private class TestableNotificationData extends NotificationData {
-        public TestableNotificationData(Environment environment) {
-            super(environment);
+        public TestableNotificationData() {
+            super();
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 4e16b7f..fb4ecd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -54,7 +54,9 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
@@ -66,11 +68,14 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -96,6 +101,7 @@
     private static final int TEST_UID = 0;
 
     @Mock private NotificationPresenter mPresenter;
+    @Mock private KeyguardEnvironment mEnvironment;
     @Mock private ExpandableNotificationRow mRow;
     @Mock private NotificationListContainer mListContainer;
     @Mock private NotificationEntryManager.Callback mCallback;
@@ -134,8 +140,9 @@
         }
 
         @Override
-        public void onAsyncInflationFinished(NotificationData.Entry entry) {
-            super.onAsyncInflationFinished(entry);
+        public void onAsyncInflationFinished(NotificationData.Entry entry,
+                @NotificationInflater.InflationFlag int inflatedFlags) {
+            super.onAsyncInflationFinished(entry, inflatedFlags);
 
             mCountDownLatch.countDown();
         }
@@ -188,6 +195,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mDependency.injectMockDependency(ShadeController.class);
         mDependency.injectTestDependency(ForegroundServiceController.class,
                 mForegroundServiceController);
         mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
@@ -202,12 +210,12 @@
         mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
+        mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
 
         mCountDownLatch = new CountDownLatch(1);
 
-        when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
-        when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(mLockscreenUserManager);
-        when(mPresenter.getGroupManager()).thenReturn(mGroupManager);
+        mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
+                Handler.createAsync(Looper.myLooper()));
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
         when(mListContainer.getViewParentForNotification(any())).thenReturn(
                 new FrameLayout(mContext));
@@ -222,6 +230,7 @@
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
         mEntryManager = new TestableNotificationEntryManager(mContext, mBarService);
+        Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mCallback, mHeadsUpManager);
 
         setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
@@ -419,8 +428,9 @@
 
     @Test
     public void testUpdateNotificationRanking() {
-        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
-        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
         mEntry.row = mRow;
         mEntry.setInflationTask(mAsyncInflationTask);
@@ -428,51 +438,51 @@
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow).updateNotification(eq(mEntry));
+        verify(mRow).setEntry(eq(mEntry));
         assertEquals(1, mEntry.smartActions.size());
         assertEquals("action", mEntry.smartActions.get(0).title);
     }
 
     @Test
     public void testUpdateNotificationRanking_noChange() {
-        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
-        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
         mEntry.row = mRow;
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, null);
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow, never()).updateNotification(eq(mEntry));
+        verify(mRow, never()).setEntry(eq(mEntry));
         assertEquals(0, mEntry.smartActions.size());
     }
 
     @Test
     public void testUpdateNotificationRanking_rowNotInflatedYet() {
-        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
-        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
         mEntry.row = null;
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow, never()).updateNotification(eq(mEntry));
+        verify(mRow, never()).setEntry(eq(mEntry));
         assertEquals(1, mEntry.smartActions.size());
         assertEquals("action", mEntry.smartActions.get(0).title);
     }
 
     @Test
     public void testUpdateNotificationRanking_pendingNotification() {
-        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
-        when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+        when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
         mEntry.row = null;
         mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow, never()).updateNotification(eq(mEntry));
+        verify(mRow, never()).setEntry(eq(mEntry));
         assertEquals(1, mEntry.smartActions.size());
         assertEquals("action", mEntry.smartActions.get(0).title);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index ca62c3b..1c7a8e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -92,7 +92,7 @@
         mEntry.row = mRow;
 
         mLogger = new TestableNotificationLogger(mBarService);
-        mLogger.setUpWithEntryManager(mEntryManager, mListContainer);
+        mLogger.setUpWithContainer(mListContainer);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 743b307..bf8eb62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,8 +18,13 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -48,6 +53,7 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -120,7 +126,8 @@
 
     @Test
     public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception {
-        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow());
+        ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow(
+                FLAG_CONTENT_VIEW_ALL));
         row.setSensitive(true, true);
         row.setHideSensitive(true, false, 0, 0);
         verify(row).updateShelfIconColor();
@@ -133,6 +140,38 @@
         verify(row).updateShelfIconColor();
     }
 
+    // TODO: Ignoring as a temporary workaround until heads up views can be safely freed.
+    // See http://b/117933032
+    @Test
+    @Ignore
+    public void testFreeContentViewWhenSafe() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
+
+        row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+
+        assertNull(row.getPrivateLayout().getHeadsUpChild());
+    }
+
+    @Test
+    public void setNeedsRedactionSetsInflationFlag() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow();
+
+        row.setNeedsRedaction(true);
+
+        assertTrue(row.getNotificationInflater().isInflationFlagSet(FLAG_CONTENT_VIEW_PUBLIC));
+    }
+
+    @Test
+    public void setNeedsRedactionFreesViewWhenFalse() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
+        row.setNeedsRedaction(true);
+        row.getPublicLayout().setVisibility(View.GONE);
+
+        row.setNeedsRedaction(false);
+
+        assertNull(row.getPublicLayout().getContractedChild());
+    }
+
     @Test
     public void testAboveShelfChangedListenerCalled() throws Exception {
         ExpandableNotificationRow row = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 8edbf17..ee35449 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -61,7 +61,9 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -93,11 +95,14 @@
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationStackScrollLayout mStackScroller;
     @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
-    @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener;
+    @Mock private OnSettingsClickListener mOnSettingsClickListener;
+    @Mock private DeviceProvisionedController mDeviceProvisionedController;
 
     @Before
     public void setUp() {
         mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(DeviceProvisionedController.class,
+                mDeviceProvisionedController);
         mHandler = Handler.createAsync(mTestableLooper.getLooper());
 
         mHelper = new NotificationTestHelper(mContext);
@@ -330,7 +335,7 @@
         row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
-        when(mPresenter.isDeviceProvisioned()).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
         mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index 81e79d1..fdb66cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -16,10 +16,15 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_ALL;
-
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -34,6 +39,7 @@
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.RemoteViews;
@@ -41,8 +47,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationData;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -82,7 +88,8 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry) {
+            public void onAsyncInflationFinished(NotificationData.Entry entry,
+                    @NotificationInflater.InflationFlag int inflatedFlags) {
             }
         });
     }
@@ -91,7 +98,7 @@
     public void testIncreasedHeadsUpBeingUsed() {
         mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
         verify(builder).createHeadsUpContentView(true);
     }
 
@@ -99,7 +106,7 @@
     public void testIncreasedHeightBeingUsed() {
         mNotificationInflater.setUsesIncreasedHeight(true);
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
         verify(builder).createContentView(true);
     }
 
@@ -110,15 +117,18 @@
         verify(mRow).onNotificationUpdated();
     }
 
+    // TODO: Ignoring as a temporary workaround until ambient views can be safely freed.
+    // See http://b/117894786
     @Test
-    public void testInflationCallsOnlyRightMethod() throws Exception {
-        mRow.getPrivateLayout().removeAllViews();
-        mRow.getEntry().cachedBigContentView = null;
-        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
-                FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
-        assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
-        assertTrue(mRow.getPrivateLayout().getChildAt(0)
-                == mRow.getPrivateLayout().getExpandedChild());
+    @Ignore
+    public void testInflationOnlyInflatesSetFlags() throws Exception {
+        mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP,
+                true /* shouldInflate */);
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+                mNotificationInflater);
+
+        assertNotNull(mRow.getPrivateLayout().getHeadsUpChild());
+        assertNull(mRow.getShowingLayout().getAmbientChild());
         verify(mRow).onNotificationUpdated();
     }
 
@@ -155,8 +165,9 @@
                 new NotificationInflater.InflationProgress();
         result.packageContext = mContext;
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        NotificationInflater.applyRemoteView(result, FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
-                false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
+        NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+                new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
+                true /* isNewView */, new RemoteViews.OnClickHandler(),
                 new NotificationInflater.InflationCallback() {
                     @Override
                     public void handleInflationException(StatusBarNotification notification,
@@ -166,10 +177,11 @@
                     }
 
                     @Override
-                    public void onAsyncInflationFinished(NotificationData.Entry entry) {
+                    public void onAsyncInflationFinished(NotificationData.Entry entry,
+                            @NotificationInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
-                }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
+                }, mRow.getPrivateLayout(), null, null, new HashMap<>(),
                 new NotificationInflater.ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -184,18 +196,34 @@
         assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
     }
 
+    @Test
+    public void testUpdateNeedsRedactionReinflatesChangedContentViews() {
+        mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true);
+        mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true);
+        mNotificationInflater.updateNeedsRedaction(true);
+
+        NotificationInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
+        assertEquals(FLAG_CONTENT_VIEW_AMBIENT | FLAG_CONTENT_VIEW_PUBLIC,
+                asyncInflationTask.getReInflateFlags());
+        asyncInflationTask.abort();
+    }
+
     /* Cancelling requires us to be on the UI thread otherwise we might have a race */
     @Test
-    public void testSupersedesExistingTask() throws Exception {
+    public void testSupersedesExistingTask() {
+        mNotificationInflater.addInflationFlags(FLAG_CONTENT_VIEW_ALL);
         mNotificationInflater.inflateNotificationViews();
+
+        // Trigger inflation of content and expanded only.
         mNotificationInflater.setIsLowPriority(true);
         mNotificationInflater.setIsChildInGroup(true);
+
         InflationTask runningTask = mRow.getEntry().getRunningTask();
         NotificationInflater.AsyncInflationTask asyncInflationTask =
                 (NotificationInflater.AsyncInflationTask) runningTask;
-        Assert.assertSame("Successive inflations don't inherit the previous flags!",
-                asyncInflationTask.getReInflateFlags(),
-                NotificationInflater.FLAG_REINFLATE_ALL);
+        assertEquals("Successive inflations don't inherit the previous flags!",
+                FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags());
         runningTask.abort();
     }
 
@@ -231,7 +259,8 @@
             }
 
             @Override
-            public void onAsyncInflationFinished(NotificationData.Entry entry) {
+            public void onAsyncInflationFinished(NotificationData.Entry entry,
+                    @NotificationInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
                             "Inflation finished even though there should be an error"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index f8b2436..d2fe82f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -17,6 +17,8 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -40,30 +42,31 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.ExpandHelper;
+import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarTest.TestableNotificationEntryManager;
 
-import java.util.ArrayList;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -74,6 +77,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
+
 /**
  * Tests for {@link NotificationStackScrollLayout}.
  */
@@ -108,6 +113,7 @@
         mDependency.injectTestDependency(StatusBarStateController.class, mBarState);
         mDependency.injectTestDependency(NotificationRemoteInputManager.class,
                 mRemoteInputManager);
+        mDependency.injectMockDependency(ShadeController.class);
         when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
 
         IPowerManager powerManagerService = mock(IPowerManager.class);
@@ -116,11 +122,13 @@
 
         mEntryManager = new TestableNotificationEntryManager(mDreamManager, mPowerManager,
                 mContext);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+        Dependency.get(InitController.class).executePostInitTasks();
         mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, mHeadsUpManager,
                 mNotificationData);
-        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
 
-        NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
+
+        NotificationShelf notificationShelf = mock(NotificationShelf.class);
         mStackScroller = spy(new NotificationStackScrollLayout(getContext()));
         mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
@@ -147,7 +155,7 @@
         when(mBarState.getState()).thenReturn(StatusBarState.SHADE);
         mStackScroller.setDimmed(true /* dimmed */, false /* animate */);
         mStackScroller.setDimmed(true /* dimmed */, true /* animate */);
-        Assert.assertFalse(mStackScroller.isDimmed());
+        assertFalse(mStackScroller.isDimmed());
     }
 
     @Test
@@ -246,7 +254,7 @@
         assertEquals(0, mNotificationData.getActiveNotifications().size());
 
         mStackScroller.updateFooter();
-        verify(mStackScroller).updateFooterView(false, false);
+        verify(mStackScroller, atLeastOnce()).updateFooterView(false, false);
     }
 
     @Test
@@ -287,7 +295,8 @@
         setBarStateForTest(StatusBarState.SHADE);
         ArrayList<Entry> entries = new ArrayList<>();
         entries.add(mock(Entry.class));
-        when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+        when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
+        assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
 
         mStackScroller.updateFooter();
         verify(mStackScroller).updateFooterView(true, false);
@@ -348,9 +357,8 @@
     }
 
     private void setBarStateForTest(int state) {
-        ArgumentCaptor<StatusBarStateController.StateListener> captor =
-                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
-        verify(mBarState, atLeastOnce()).addListener(captor.capture());
-        captor.getValue().onStateChanged(state);
+        // Can't inject this through the listener or we end up on the actual implementation
+        // rather than the mock because the spy just coppied the anonymous inner /shruggie.
+        mStackScroller.setStatusBarState(state);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
new file mode 100644
index 0000000..96f1976
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -0,0 +1,47 @@
+package com.android.systemui.statusbar.phone
+
+import android.support.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.KeyguardIndicationController
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardBottomAreaTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var mStatusBar: StatusBar
+    @Mock
+    private lateinit var mKeyguardIndicationController: KeyguardIndicationController
+    private lateinit var mKeyguardBottomArea: KeyguardBottomAreaView
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
+                R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
+        mKeyguardBottomArea.setStatusBar(mStatusBar)
+        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController)
+    }
+
+    @Test
+    fun initFrom_doesntCrash() {
+        val other = LayoutInflater.from(mContext).inflate(
+                R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
+
+        other.initFrom(mKeyguardBottomArea)
+        other.launchVoiceAssist()
+        other.onLongClick(null)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 020682b6..7f8668f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -238,7 +239,7 @@
 
     @Test
     public void testShowOnDismissAction_showsBouncer() {
-        final KeyguardHostView.OnDismissAction dismissAction = () -> false;
+        final OnDismissAction dismissAction = () -> false;
         final Runnable cancelAction = () -> {};
         mBouncer.showWithDismissAction(dismissAction, cancelAction);
         verify(mKeyguardHostView).setOnDismissAction(dismissAction, cancelAction);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
new file mode 100644
index 0000000..be26d91
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarRotationContextTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TestableDependency;
+import com.android.systemui.statusbar.policy.KeyButtonDrawable;
+import com.android.systemui.statusbar.policy.RotationLockController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+/** atest NavigationBarRotationContextTest */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NavigationBarRotationContextTest extends SysuiTestCase {
+    static final int RES_UNDEF = 0;
+    static final int DEFAULT_ROTATE = 0;
+
+    @Rule
+    public final SysuiTestableContext mContext = new SysuiTestableContext(
+            InstrumentationRegistry.getContext(), getLeakCheck());
+    private final TestableDependency mDependency = new TestableDependency(mContext);
+    private RotationContextButton mButton;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mDependency.injectMockDependency(RotationLockController.class);
+
+        final View view = new View(mContext);
+        mButton = spy(new RotationContextButton(RES_UNDEF, RES_UNDEF, mContext, RES_UNDEF));
+        final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
+        doReturn(view).when(mButton).getCurrentView();
+        doReturn(kbd).when(mButton).getNewDrawable();
+    }
+
+    @Test
+    public void testOnInvalidRotationProposal() {
+        mButton.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE + 1, false /* isValid */);
+        verify(mButton, times(1)).setRotateSuggestionButtonState(false /* visible */);
+    }
+
+    @Test
+    public void testOnSameRotationProposal() {
+        mButton.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE, true /* isValid */);
+        verify(mButton, times(1)).setRotateSuggestionButtonState(false /* visible */);
+    }
+
+    @Test
+    public void testOnRotationProposalShowButtonShowNav() {
+        // No navigation bar should not call to set visibility state
+        mButton.onNavigationBarWindowVisibilityChange(false /* showing */);
+        verify(mButton, times(0)).setRotateSuggestionButtonState(false /* visible */);
+        verify(mButton, times(0)).setRotateSuggestionButtonState(true /* visible */);
+
+        // No navigation bar with rotation change should not call to set visibility state
+        mButton.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE + 1, true /* isValid */);
+        verify(mButton, times(0)).setRotateSuggestionButtonState(false /* visible */);
+        verify(mButton, times(0)).setRotateSuggestionButtonState(true /* visible */);
+
+        // Since rotation has changed rotation should be pending, show mButton when showing nav bar
+        mButton.onNavigationBarWindowVisibilityChange(true /* showing */);
+        verify(mButton, times(1)).setRotateSuggestionButtonState(true /* visible */);
+    }
+
+    @Test
+    public void testOnRotationProposalShowButton() {
+        // Navigation bar being visible should not call to set visibility state
+        mButton.onNavigationBarWindowVisibilityChange(true /* showing */);
+        verify(mButton, times(0)).setRotateSuggestionButtonState(false /* visible */);
+        verify(mButton, times(0)).setRotateSuggestionButtonState(true /* visible */);
+
+        // Navigation bar is visible and rotation requested
+        mButton.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE + 1, true /* isValid */);
+        verify(mButton, times(1)).setRotateSuggestionButtonState(true /* visible */);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index be4560b..93dedfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -31,10 +31,10 @@
 import android.view.ViewGroup;
 
 import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardHostView;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -78,7 +78,7 @@
 
     @Test
     public void dismissWithAction_AfterKeyguardGoneSetToFalse() {
-        KeyguardHostView.OnDismissAction action = () -> false;
+        OnDismissAction action = () -> false;
         Runnable cancelAction = () -> {};
         mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
                 false /* afterKeyguardGone */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
new file mode 100644
index 0000000..24baa7d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.mock;
+
+import android.app.Notification;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.metrics.LogMaker;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.support.test.metricshelper.MetricsAsserts;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.ViewGroup;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class StatusBarNotificationPresenterTest extends SysuiTestCase {
+
+
+    private StatusBarNotificationPresenter mStatusBar;
+    private CommandQueue mCommandQueue;
+    private FakeMetricsLogger mMetricsLogger;
+    private ShadeController mShadeController = mock(ShadeController.class);
+
+    @Before
+    public void setup() {
+        mMetricsLogger = new FakeMetricsLogger();
+        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+        mCommandQueue = new CommandQueue();
+        mContext.putComponent(CommandQueue.class, mCommandQueue);
+        mDependency.injectTestDependency(ShadeController.class, mShadeController);
+
+        mStatusBar = new StatusBarNotificationPresenter(mContext,
+                mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
+                mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class),
+                mock(DozeScrimController.class), mock(ScrimController.class),
+                mock(ActivityLaunchAnimator.Callback.class));
+    }
+
+    @Test
+    public void testHeadsUp_disabledStatusBar() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        mCommandQueue.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
+        TestableLooper.get(this).processAllMessages();
+
+        assertFalse("The panel shouldn't allow heads up while disabled",
+                mStatusBar.canHeadsUp(entry, sbn));
+    }
+
+    @Test
+    public void testHeadsUp_disabledNotificationShade() {
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        mCommandQueue.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
+        TestableLooper.get(this).processAllMessages();
+
+        assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
+                mStatusBar.canHeadsUp(entry, sbn));
+    }
+
+    @Test
+    public void onActivatedMetrics() {
+        ActivatableNotificationView view =  mock(ActivatableNotificationView.class);
+        mStatusBar.onActivated(view);
+
+        MetricsAsserts.assertHasLog("missing lockscreen note tap log",
+                mMetricsLogger.getLogs(),
+                new LogMaker(MetricsEvent.ACTION_LS_NOTE)
+                        .setType(MetricsEvent.TYPE_ACTION));
+    }
+
+    // We need this because mockito doesn't know how to construct a mock that extends ViewGroup
+    // and implements NotificationListContainer without it because of classloader issues.
+    private abstract static class NotificationListContainerViewGroup extends ViewGroup
+            implements NotificationListContainer {
+
+        public NotificationListContainerViewGroup(Context context) {
+            super(context);
+        }
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
new file mode 100644
index 0000000..f28757f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
+    @Mock private NotificationPresenter mPresenter;
+    @Mock private UserManager mUserManager;
+
+    // Dependency mocks:
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private DeviceProvisionedController mDeviceProvisionedController;
+    @Mock private ShadeController mShadeController;
+    @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+
+    private int mCurrentUserId = 0;
+    private StatusBarRemoteInputCallback mRemoteInputCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+        mDependency.injectTestDependency(DeviceProvisionedController.class,
+                mDeviceProvisionedController);
+        mDependency.injectTestDependency(ShadeController.class, mShadeController);
+        mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
+                mNotificationLockscreenUserManager);
+        mDependency.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+        mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext));
+        mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
+    }
+
+    @Test
+    public void testActionDeviceLockedChangedWithDifferentUserIdCallsOnWorkChallengeChanged() {
+        when(mNotificationLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId);
+        when(mNotificationLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(true);
+        Intent intent = new Intent()
+                .setAction(ACTION_DEVICE_LOCKED_CHANGED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1);
+        mRemoteInputCallback.mChallengeReceiver.onReceive(mContext, intent);
+        verify(mRemoteInputCallback, times(1)).onWorkChallengeChanged();
+    }
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index da93327..939245f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -18,9 +18,7 @@
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
-import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.fail;
 
@@ -64,42 +62,41 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.Dependency;
 import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
-import com.android.systemui.statusbar.notification.AppOpsListener;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.AppOpsListener;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -137,18 +134,21 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
     @Mock private StatusBarStateController mStatusBarStateController;
+    @Mock private DeviceProvisionedController mDeviceProvisionedController;
+    @Mock private NotificationPresenter mNotificationPresenter;
+    @Mock private NotificationEntryManager.Callback mCallback;
 
     private TestableStatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
     private PowerManager mPowerManager;
     private TestableNotificationEntryManager mEntryManager;
     private NotificationLogger mNotificationLogger;
+    private CommandQueue mCommandQueue;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(AssistManager.class);
-        mDependency.injectMockDependency(DeviceProvisionedController.class);
         mDependency.injectMockDependency(NotificationGutsManager.class);
         mDependency.injectMockDependency(NotificationMediaManager.class);
         mDependency.injectMockDependency(ForegroundServiceController.class);
@@ -159,6 +159,8 @@
         mDependency.injectTestDependency(KeyguardMonitor.class, mock(KeyguardMonitorImpl.class));
         mDependency.injectTestDependency(AppOpsListener.class, mock(AppOpsListener.class));
         mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
+        mDependency.injectTestDependency(DeviceProvisionedController.class,
+                mDeviceProvisionedController);
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -172,9 +174,9 @@
         mPowerManager = new PowerManager(mContext, powerManagerService,
                 Handler.createAsync(Looper.myLooper()));
 
-        CommandQueue commandQueue = mock(CommandQueue.class);
-        when(commandQueue.asBinder()).thenReturn(new Binder());
-        mContext.putComponent(CommandQueue.class, commandQueue);
+        mCommandQueue = mock(CommandQueue.class);
+        when(mCommandQueue.asBinder()).thenReturn(new Binder());
+        mContext.putComponent(CommandQueue.class, mCommandQueue);
 
         mContext.setTheme(R.style.Theme_SystemUI_Light);
 
@@ -202,17 +204,19 @@
                 mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
                 mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
                 mEntryManager, mScrimController, mBiometricUnlockController,
-                mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
-                mRemoteInputManager, mock(NotificationGroupManager.class),
+                mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class),
                 mock(FalsingManager.class), mock(StatusBarWindowController.class),
                 mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
                 mock(NotificationShelf.class), mLockscreenUserManager,
-                mock(CommandQueue.class));
+                mCommandQueue,
+                mNotificationPresenter);
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
-        mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager,
-                mNotificationData);
-        mNotificationLogger.setUpWithEntryManager(mEntryManager, mStackScroller);
+        mStatusBar.putComponent(StatusBar.class, mStatusBar);
+        Dependency.get(InitController.class).executePostInitTasks();
+        mEntryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
+                mCallback, mHeadsUpManager, mNotificationData);
+        mNotificationLogger.setUpWithContainer(mStackScroller);
 
         TestableLooper.get(this).setMessageHandler(m -> {
             if (m.getCallback() == mStatusBar.mNotificationLogger.getVisibilityReporter()) {
@@ -347,17 +351,6 @@
     }
 
     @Test
-    public void onActivatedMetrics() {
-        ActivatableNotificationView view =  mock(ActivatableNotificationView.class);
-        mStatusBar.onActivated(view);
-
-        MetricsAsserts.assertHasLog("missing lockscreen note tap log",
-                mMetricsLogger.getLogs(),
-                new LogMaker(MetricsEvent.ACTION_LS_NOTE)
-                        .setType(MetricsEvent.TYPE_ACTION));
-    }
-
-    @Test
     public void testShouldHeadsUp_nonSuppressedGroupSummary() throws Exception {
         when(mPowerManager.isScreenOn()).thenReturn(true);
         when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
@@ -365,6 +358,7 @@
         when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
         when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -386,6 +380,7 @@
         when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
         when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
 
         Notification n = new Notification.Builder(getContext(), "a")
                 .setGroup("a")
@@ -406,6 +401,7 @@
         when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
         when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
 
         when(mNotificationData.shouldSuppressPeek(any())).thenReturn(true);
 
@@ -424,6 +420,7 @@
         when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
         when(mDreamManager.isDreaming()).thenReturn(false);
         when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+        when(mCallback.canHeadsUp(any(), any())).thenReturn(true);
 
         when(mNotificationData.shouldSuppressPeek(any())).thenReturn(false);
 
@@ -436,30 +433,6 @@
     }
 
     @Test
-    public void testHeadsUp_disabledStatusBar() {
-        Notification n = new Notification.Builder(getContext(), "a").build();
-        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
-                UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        mStatusBar.disable(StatusBarManager.DISABLE_EXPAND, 0, false /* animate */);
-
-        assertFalse("The panel shouldn't allow heads up while disabled",
-                mStatusBar.canHeadsUp(entry, sbn));
-    }
-
-    @Test
-    public void testHeadsUp_disabledNotificationShade() {
-        Notification n = new Notification.Builder(getContext(), "a").build();
-        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
-                UserHandle.of(0), null, 0);
-        NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        mStatusBar.disable(0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false /* animate */);
-
-        assertFalse("The panel shouldn't allow heads up while notification shade disabled",
-                mStatusBar.canHeadsUp(entry, sbn));
-    }
-
-    @Test
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
@@ -473,10 +446,11 @@
 
     @Test
     public void testPanelOpenForHeadsUp() {
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
         when(mNotificationList.size()).thenReturn(5);
-        when(mNotificationPanelView.isFullyCollapsed()).thenReturn(true);
+        when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
         mStatusBar.setBarStateForTest(StatusBarState.SHADE);
 
         try {
@@ -495,7 +469,7 @@
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
         when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
         when(mNotificationList.size()).thenReturn(5);
-        when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false);
+        when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
         mStatusBar.setBarStateForTest(StatusBarState.SHADE);
 
         try {
@@ -514,7 +488,7 @@
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
         when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList);
         when(mNotificationList.size()).thenReturn(5);
-        when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false);
+        when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
         mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
 
         try {
@@ -532,8 +506,9 @@
     public void testDisableExpandStatusBar() {
         mStatusBar.setBarStateForTest(StatusBarState.SHADE);
         mStatusBar.setUserSetupForTest(true);
-        when(mStatusBar.isDeviceProvisioned()).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
+        when(mCommandQueue.panelsEnabled()).thenReturn(false);
         mStatusBar.disable(StatusBarManager.DISABLE_NONE,
                 StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
         verify(mNotificationPanelView).setQsExpansionEnabled(false);
@@ -542,6 +517,7 @@
         mStatusBar.animateExpandSettingsPanel(null);
         verify(mNotificationPanelView, never()).expand(anyBoolean());
 
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
         mStatusBar.disable(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE, false);
         verify(mNotificationPanelView).setQsExpansionEnabled(true);
         mStatusBar.animateExpandNotificationsPanel();
@@ -627,7 +603,7 @@
                 NotificationViewHierarchyManager viewHierarchyManager,
                 TestableNotificationEntryManager entryManager, ScrimController scrimController,
                 BiometricUnlockController biometricUnlockController,
-                ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
+                KeyguardViewMediator keyguardViewMediator,
                 NotificationRemoteInputManager notificationRemoteInputManager,
                 NotificationGroupManager notificationGroupManager,
                 FalsingManager falsingManager,
@@ -636,7 +612,8 @@
                 DozeScrimController dozeScrimController,
                 NotificationShelf notificationShelf,
                 NotificationLockscreenUserManager notificationLockscreenUserManager,
-                CommandQueue commandQueue) {
+                CommandQueue commandQueue,
+                NotificationPresenter notificationPresenter) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -653,7 +630,6 @@
             mEntryManager = entryManager;
             mScrimController = scrimController;
             mBiometricUnlockController = biometricUnlockController;
-            mActivityLaunchAnimator = launchAnimator;
             mKeyguardViewMediator = keyguardViewMediator;
             mRemoteInputManager = notificationRemoteInputManager;
             mGroupManager = notificationGroupManager;
@@ -664,6 +640,7 @@
             mNotificationShelf = notificationShelf;
             mLockscreenUserManager = notificationLockscreenUserManager;
             mCommandQueue = commandQueue;
+            mPresenter = notificationPresenter;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 445a194..46335dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -17,11 +17,11 @@
 package com.android.systemui.statusbar.phone;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.MotionEvent;
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,11 +44,14 @@
     private StatusBarWindowView mView;
     private StatusBar mStatusBar;
     private DragDownHelper mDragDownHelper;
+    private NotificationStackScrollLayout mStackScrollLayout;
 
     @Before
     public void setUp() {
         mDependency.injectMockDependency(StatusBarStateController.class);
-        mView = new StatusBarWindowView(getContext(), null);
+        mView = spy(new StatusBarWindowView(getContext(), null));
+        mStackScrollLayout = mock(NotificationStackScrollLayout.class);
+        when(mView.getStackScrollLayout()).thenReturn(mStackScrollLayout);
         mStatusBar = mock(StatusBar.class);
         mView.setService(mStatusBar);
         mDragDownHelper = mock(DragDownHelper.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 01e6307..4534ebe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -41,7 +41,7 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 
-import com.android.keyguard.KeyguardHostView.OnDismissAction;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationData;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
index 51b86c2..95c7a4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -62,6 +62,11 @@
     }
 
     @Override
+    public boolean isLaunchTransitionFadingAway() {
+        return false;
+    }
+
+    @Override
     public long getKeyguardFadingAwayDuration() {
         return 0;
     }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index d86de5d..da1fee3 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6564,6 +6564,11 @@
     // OS: Q
     MOBILE_ROAMING_DIALOG = 1583;
 
+    // OPEN: Settings > Display > Lock screen display > On lock screen
+    // CATEGORY: SETTINGS
+    // OS: Q
+    LOCK_SCREEN_NOTIFICATION_CONTENT = 1584;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index b938f3b..17bf570 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -32,6 +32,8 @@
 import android.text.TextUtils;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Display;
 import android.view.MagnificationSpec;
 import android.view.View;
 import android.view.animation.DecelerateInterpolator;
@@ -40,6 +42,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -55,7 +58,7 @@
  * magnification region. If a value is out of bounds, it will be adjusted to guarantee these
  * constraints.
  */
-public class MagnificationController implements Handler.Callback {
+public class MagnificationController {
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "MagnificationController";
 
@@ -64,90 +67,553 @@
 
     private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
 
-    private static final int INVALID_ID = -1;
-
     private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f;
 
-    // Messages
-    private static final int MSG_SEND_SPEC_TO_ANIMATION = 1;
-    private static final int MSG_SCREEN_TURNED_OFF = 2;
-    private static final int MSG_ON_MAGNIFIED_BOUNDS_CHANGED = 3;
-    private static final int MSG_ON_RECTANGLE_ON_SCREEN_REQUESTED = 4;
-    private static final int MSG_ON_USER_CONTEXT_CHANGED = 5;
-
     private final Object mLock;
 
-    /**
-     * The current magnification spec. If an animation is running, this
-     * reflects the end state.
-     */
-    private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
-
-    private final Region mMagnificationRegion = Region.obtain();
-    private final Rect mMagnificationBounds = new Rect();
-
-    private final Rect mTempRect = new Rect();
-    private final Rect mTempRect1 = new Rect();
-
     private final AccessibilityManagerService mAms;
 
     private final SettingsBridge mSettingsBridge;
 
     private final ScreenStateObserver mScreenStateObserver;
 
-    private final SpecAnimationBridge mSpecAnimationBridge;
-
-    private final WindowManagerInternal.MagnificationCallbacks mWMCallbacks =
-            new WindowManagerInternal.MagnificationCallbacks () {
-                @Override
-                public void onMagnificationRegionChanged(Region region) {
-                    final SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = Region.obtain(region);
-                    mHandler.obtainMessage(MSG_ON_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
-                }
-
-                @Override
-                public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
-                    final SomeArgs args = SomeArgs.obtain();
-                    args.argi1 = left;
-                    args.argi2 = top;
-                    args.argi3 = right;
-                    args.argi4 = bottom;
-                    mHandler.obtainMessage(MSG_ON_RECTANGLE_ON_SCREEN_REQUESTED, args)
-                            .sendToTarget();
-                }
-
-                @Override
-                public void onRotationChanged(int rotation) {
-                    // Treat as context change and reset
-                    mHandler.sendEmptyMessage(MSG_ON_USER_CONTEXT_CHANGED);
-                }
-
-                @Override
-                public void onUserContextChanged() {
-                    mHandler.sendEmptyMessage(MSG_ON_USER_CONTEXT_CHANGED);
-                }
-            };
-
     private int mUserId;
 
     private final long mMainThreadId;
 
     private Handler mHandler;
 
-    private int mIdOfLastServiceToMagnify = INVALID_ID;
-
     private final WindowManagerInternal mWindowManager;
 
-    // Flag indicating that we are registered with window manager.
-    @VisibleForTesting boolean mRegistered;
+    private final DisplayMagnification mDisplay;
 
-    private boolean mUnregisterPending;
+    /**
+     * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
+     * magnification information per display.
+     */
+    private final class DisplayMagnification implements
+            WindowManagerInternal.MagnificationCallbacks {
+        /**
+         * The current magnification spec. If an animation is running, this
+         * reflects the end state.
+         */
+        private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain();
+
+        private final Region mMagnificationRegion = Region.obtain();
+        private final Rect mMagnificationBounds = new Rect();
+
+        private final Rect mTempRect = new Rect();
+        private final Rect mTempRect1 = new Rect();
+
+        private final SpecAnimationBridge mSpecAnimationBridge;
+
+        // Flag indicating that we are registered with window manager.
+        private boolean mRegistered;
+        private boolean mUnregisterPending;
+
+        private final int mDisplayId;
+
+        private static final int INVALID_ID = -1;
+        private int mIdOfLastServiceToMagnify = INVALID_ID;
+
+
+        DisplayMagnification(int displayId, SpecAnimationBridge specAnimation) {
+            mDisplayId = displayId;
+            mSpecAnimationBridge = specAnimation;
+        }
+
+        void register() {
+            synchronized (mLock) {
+                if (!mRegistered) {
+                    mWindowManager.setMagnificationCallbacks(this);
+                    mSpecAnimationBridge.setEnabled(true);
+                    // Obtain initial state.
+                    mWindowManager.getMagnificationRegion(mMagnificationRegion);
+                    mMagnificationRegion.getBounds(mMagnificationBounds);
+                    mRegistered = true;
+                }
+            }
+        }
+
+        void unregister() {
+            synchronized (mLock) {
+                if (!isMagnifying()) {
+                    unregisterInternalLocked();
+                } else {
+                    mUnregisterPending = true;
+                    reset(true);
+                }
+            }
+        }
+
+        boolean isRegisteredLocked() {
+            return mRegistered;
+        }
+
+
+        float getScale() {
+            return mCurrentMagnificationSpec.scale;
+        }
+
+        float getOffsetX() {
+            return mCurrentMagnificationSpec.offsetX;
+        }
+
+        float getCenterX() {
+            synchronized (mLock) {
+                return (mMagnificationBounds.width() / 2.0f
+                        + mMagnificationBounds.left - getOffsetX()) / getScale();
+            }
+        }
+
+        float getCenterY() {
+            synchronized (mLock) {
+                return (mMagnificationBounds.height() / 2.0f
+                        + mMagnificationBounds.top - getOffsetY()) / getScale();
+            }
+        }
+
+        /**
+         * Returns the scale currently used by the window manager. If an
+         * animation is in progress, this reflects the current state of the
+         * animation.
+         *
+         * @return the scale currently used by the window manager
+         */
+        float getSentScale() {
+            return mSpecAnimationBridge.mSentMagnificationSpec.scale;
+        }
+
+        /**
+         * Returns the X offset currently used by the window manager. If an
+         * animation is in progress, this reflects the current state of the
+         * animation.
+         *
+         * @return the X offset currently used by the window manager
+         */
+        float getSentOffsetX() {
+            return mSpecAnimationBridge.mSentMagnificationSpec.offsetX;
+        }
+
+        /**
+         * Returns the Y offset currently used by the window manager. If an
+         * animation is in progress, this reflects the current state of the
+         * animation.
+         *
+         * @return the Y offset currently used by the window manager
+         */
+        float getSentOffsetY() {
+            return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
+        }
+
+        boolean resetIfNeeded(boolean animate) {
+            synchronized (mLock) {
+                if (isMagnifying()) {
+                    reset(animate);
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        float getOffsetY() {
+            return mCurrentMagnificationSpec.offsetY;
+        }
+
+        boolean isMagnifying() {
+            return mCurrentMagnificationSpec.scale > 1.0f;
+        }
+
+        void unregisterInternalLocked() {
+            if (mRegistered) {
+                mSpecAnimationBridge.setEnabled(false);
+                mWindowManager.setMagnificationCallbacks(null);
+                mMagnificationRegion.setEmpty();
+
+                mRegistered = false;
+            }
+            mUnregisterPending = false;
+        }
+
+
+        @Override
+        public void onMagnificationRegionChanged(Region magnificationRegion) {
+            final Message m = PooledLambda.obtainMessage(
+                    DisplayMagnification.this::updateMagnificationRegion,
+                    Region.obtain(magnificationRegion));
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
+            final Message m = PooledLambda.obtainMessage(
+                    DisplayMagnification.this::requestRectangleOnScreen, left, top, right, bottom);
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public void onRotationChanged(int rotation) {
+            // Treat as context change and reset
+            final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
+                    true);
+            mHandler.sendMessage(m);
+        }
+
+        @Override
+        public void onUserContextChanged() {
+            final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
+                    true);
+            mHandler.sendMessage(m);
+        }
+
+        /**
+         * Update our copy of the current magnification region
+         *
+         * @param magnified the magnified region
+         */
+        void updateMagnificationRegion(Region magnified) {
+            synchronized (mLock) {
+                if (!mRegistered) {
+                    // Don't update if we've unregistered
+                    return;
+                }
+                if (!mMagnificationRegion.equals(magnified)) {
+                    mMagnificationRegion.set(magnified);
+                    mMagnificationRegion.getBounds(mMagnificationBounds);
+                    // It's possible that our magnification spec is invalid with the new bounds.
+                    // Adjust the current spec's offsets if necessary.
+                    if (updateCurrentSpecWithOffsetsLocked(
+                            mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) {
+                        sendSpecToAnimation(mCurrentMagnificationSpec, false);
+                    }
+                    onMagnificationChangedLocked();
+                }
+                magnified.recycle();
+            }
+        }
+
+        void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
+            if (DEBUG) {
+                Slog.i(LOG_TAG,
+                        "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
+            }
+            if (Thread.currentThread().getId() == mMainThreadId) {
+                mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
+            } else {
+                final Message m = PooledLambda.obtainMessage(
+                        this.mSpecAnimationBridge::updateSentSpecMainThread, spec, animate);
+                mHandler.sendMessage(m);
+            }
+        }
+
+        /**
+         * Get the ID of the last service that changed the magnification spec.
+         *
+         * @return The id
+         */
+        int getIdOfLastServiceToMagnify() {
+            return mIdOfLastServiceToMagnify;
+        }
+
+        void onMagnificationChangedLocked() {
+            mAms.notifyMagnificationChanged(mMagnificationRegion,
+                    getScale(), getCenterX(), getCenterY());
+            if (mUnregisterPending && !isMagnifying()) {
+                unregisterInternalLocked();
+            }
+        }
+
+        boolean magnificationRegionContains(float x, float y) {
+            synchronized (mLock) {
+                return mMagnificationRegion.contains((int) x, (int) y);
+
+            }
+        }
+
+        void getMagnificationBounds(@NonNull Rect outBounds) {
+            synchronized (mLock) {
+                outBounds.set(mMagnificationBounds);
+            }
+        }
+
+        void getMagnificationRegion(@NonNull Region outRegion) {
+            synchronized (mLock) {
+                outRegion.set(mMagnificationRegion);
+            }
+        }
+
+        void requestRectangleOnScreen(int left, int top, int right, int bottom) {
+            synchronized (mLock) {
+                final Rect magnifiedFrame = mTempRect;
+                getMagnificationBounds(magnifiedFrame);
+                if (!magnifiedFrame.intersects(left, top, right, bottom)) {
+                    return;
+                }
+
+                final Rect magnifFrameInScreenCoords = mTempRect1;
+                getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords);
+
+                final float scrollX;
+                final float scrollY;
+                if (right - left > magnifFrameInScreenCoords.width()) {
+                    final int direction = TextUtils
+                            .getLayoutDirectionFromLocale(Locale.getDefault());
+                    if (direction == View.LAYOUT_DIRECTION_LTR) {
+                        scrollX = left - magnifFrameInScreenCoords.left;
+                    } else {
+                        scrollX = right - magnifFrameInScreenCoords.right;
+                    }
+                } else if (left < magnifFrameInScreenCoords.left) {
+                    scrollX = left - magnifFrameInScreenCoords.left;
+                } else if (right > magnifFrameInScreenCoords.right) {
+                    scrollX = right - magnifFrameInScreenCoords.right;
+                } else {
+                    scrollX = 0;
+                }
+
+                if (bottom - top > magnifFrameInScreenCoords.height()) {
+                    scrollY = top - magnifFrameInScreenCoords.top;
+                } else if (top < magnifFrameInScreenCoords.top) {
+                    scrollY = top - magnifFrameInScreenCoords.top;
+                } else if (bottom > magnifFrameInScreenCoords.bottom) {
+                    scrollY = bottom - magnifFrameInScreenCoords.bottom;
+                } else {
+                    scrollY = 0;
+                }
+
+                final float scale = getScale();
+                offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID);
+            }
+        }
+
+        void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
+            final float scale = getSentScale();
+            final float offsetX = getSentOffsetX();
+            final float offsetY = getSentOffsetY();
+            getMagnificationBounds(outFrame);
+            outFrame.offset((int) -offsetX, (int) -offsetY);
+            outFrame.scale(1.0f / scale);
+        }
+
+        /**
+         * Resets magnification if last magnifying service is disabled.
+         *
+         * @param connectionId the connection ID be disabled.
+         * @return {@code true} on success, {@code false} on failure
+         */
+        boolean resetIfNeeded(int connectionId) {
+            if (mIdOfLastServiceToMagnify == connectionId) {
+                return resetIfNeeded(true /*animate*/);
+            }
+            return false;
+        }
+
+        void setForceShowMagnifiableBounds(boolean show) {
+            if (mRegistered) {
+                mWindowManager.setForceShowMagnifiableBounds(show);
+            }
+        }
+
+        boolean reset(boolean animate) {
+            synchronized (mLock) {
+                if (!mRegistered) {
+                    return false;
+                }
+                final MagnificationSpec spec = mCurrentMagnificationSpec;
+                final boolean changed = !spec.isNop();
+                if (changed) {
+                    spec.clear();
+                    onMagnificationChangedLocked();
+                }
+                mIdOfLastServiceToMagnify = INVALID_ID;
+                sendSpecToAnimation(spec, animate);
+                return changed;
+            }
+        }
+
+
+        boolean setScale(float scale, float pivotX, float pivotY,
+                boolean animate, int id) {
+
+            synchronized (mLock) {
+                if (!mRegistered) {
+                    return false;
+                }
+                // Constrain scale immediately for use in the pivot calculations.
+                scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+
+                final Rect viewport = mTempRect;
+                mMagnificationRegion.getBounds(viewport);
+                final MagnificationSpec spec = mCurrentMagnificationSpec;
+                final float oldScale = spec.scale;
+                final float oldCenterX
+                        = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
+                final float oldCenterY
+                        = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
+                final float normPivotX = (pivotX - spec.offsetX) / oldScale;
+                final float normPivotY = (pivotY - spec.offsetY) / oldScale;
+                final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+                final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+                final float centerX = normPivotX + offsetX;
+                final float centerY = normPivotY + offsetY;
+                mIdOfLastServiceToMagnify = id;
+
+                return setScaleAndCenter(scale, centerX, centerY, animate, id);
+            }
+        }
+
+        boolean setScaleAndCenter(float scale, float centerX, float centerY,
+                boolean animate, int id) {
+
+            synchronized (mLock) {
+                if (!mRegistered) {
+                    return false;
+                }
+                if (DEBUG) {
+                    Slog.i(LOG_TAG,
+                            "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
+                                    + ", centerY = " + centerY + ", animate = " + animate
+                                    + ", id = " + id
+                                    + ")");
+                }
+                final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
+                sendSpecToAnimation(mCurrentMagnificationSpec, animate);
+                if (isMagnifying() && (id != INVALID_ID)) {
+                    mIdOfLastServiceToMagnify = id;
+                }
+                return changed;
+            }
+        }
+
+        /**
+         * Updates the current magnification spec.
+         *
+         * @param scale the magnification scale
+         * @param centerX the unscaled, screen-relative X coordinate of the center
+         *                of the viewport, or {@link Float#NaN} to leave unchanged
+         * @param centerY the unscaled, screen-relative Y coordinate of the center
+         *                of the viewport, or {@link Float#NaN} to leave unchanged
+         * @return {@code true} if the magnification spec changed or {@code false}
+         *         otherwise
+         */
+        boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
+            // Handle defaults.
+            if (Float.isNaN(centerX)) {
+                centerX = getCenterX();
+            }
+            if (Float.isNaN(centerY)) {
+                centerY = getCenterY();
+            }
+            if (Float.isNaN(scale)) {
+                scale = getScale();
+            }
+
+            // Compute changes.
+            boolean changed = false;
+
+            final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+            if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
+                mCurrentMagnificationSpec.scale = normScale;
+                changed = true;
+            }
+
+            final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f
+                    + mMagnificationBounds.left - centerX * normScale;
+            final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f
+                    + mMagnificationBounds.top - centerY * normScale;
+            changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
+
+            if (changed) {
+                onMagnificationChangedLocked();
+            }
+
+            return changed;
+        }
+
+        void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
+            synchronized (mLock) {
+                if (!mRegistered) {
+                    return;
+                }
+
+                final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
+                final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
+                if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
+                    onMagnificationChangedLocked();
+                }
+                if (id != INVALID_ID) {
+                    mIdOfLastServiceToMagnify = id;
+                }
+                sendSpecToAnimation(mCurrentMagnificationSpec, false);
+            }
+        }
+
+        boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
+            if (DEBUG) {
+                Slog.i(LOG_TAG,
+                        "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
+                                + ", nonNormOffsetY = " + nonNormOffsetY + ")");
+            }
+            boolean changed = false;
+            final float offsetX = MathUtils.constrain(
+                    nonNormOffsetX, getMinOffsetXLocked(), getMaxOffsetXLocked());
+            if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
+                mCurrentMagnificationSpec.offsetX = offsetX;
+                changed = true;
+            }
+            final float offsetY = MathUtils.constrain(
+                    nonNormOffsetY, getMinOffsetYLocked(), getMaxOffsetYLocked());
+            if (Float.compare(mCurrentMagnificationSpec.offsetY, offsetY) != 0) {
+                mCurrentMagnificationSpec.offsetY = offsetY;
+                changed = true;
+            }
+            return changed;
+        }
+
+        float getMinOffsetXLocked() {
+            final float viewportWidth = mMagnificationBounds.width();
+            final float viewportLeft = mMagnificationBounds.left;
+            return (viewportLeft + viewportWidth) -
+                    (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
+        }
+
+        float getMaxOffsetXLocked() {
+            return mMagnificationBounds.left -
+                    mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
+        }
+
+        float getMinOffsetYLocked() {
+            final float viewportHeight = mMagnificationBounds.height();
+            final float viewportTop = mMagnificationBounds.top;
+            return (viewportTop + viewportHeight) -
+                    (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
+        }
+
+        float getMaxOffsetYLocked() {
+            return mMagnificationBounds.top -
+                    mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
+        }
+
+        @Override
+        public String toString() {
+            return "DisplayMagnification{" +
+                    "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
+                    ", mMagnificationRegion=" + mMagnificationRegion +
+                    ", mMagnificationBounds=" + mMagnificationBounds +
+                    ", mDisplayId=" + mDisplayId +
+                    ", mUserId=" + mUserId +
+                    ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
+                    ", mRegistered=" + mRegistered +
+                    ", mUnregisterPending=" + mUnregisterPending +
+                    '}';
+        }
+
+    }
 
     public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
         this(context, ams, lock, null, LocalServices.getService(WindowManagerInternal.class),
                 new ValueAnimator(), new SettingsBridge(context.getContentResolver()));
-        mHandler = new Handler(context.getMainLooper(), this);
+        mHandler = new Handler(context.getMainLooper());
     }
 
     public MagnificationController(
@@ -164,9 +630,10 @@
         mAms = ams;
         mScreenStateObserver = new ScreenStateObserver(context, this);
         mLock = lock;
-        mSpecAnimationBridge = new SpecAnimationBridge(
-                context, mLock, mWindowManager, valueAnimator);
         mSettingsBridge = settingsBridge;
+        //TODO (multidisplay): Magnification is supported only for the default display.
+        mDisplay =  new DisplayMagnification(Display.DEFAULT_DISPLAY,
+                new SpecAnimationBridge(context, mLock, mWindowManager, valueAnimator));
     }
 
     /**
@@ -178,16 +645,9 @@
      */
     public void register() {
         synchronized (mLock) {
-            if (!mRegistered) {
-                mScreenStateObserver.register();
-                mWindowManager.setMagnificationCallbacks(mWMCallbacks);
-                mSpecAnimationBridge.setEnabled(true);
-                // Obtain initial state.
-                mWindowManager.getMagnificationRegion(mMagnificationRegion);
-                mMagnificationRegion.getBounds(mMagnificationBounds);
-                mRegistered = true;
-            }
+            mScreenStateObserver.register();
         }
+        mDisplay.register();
     }
 
     /**
@@ -196,33 +656,18 @@
      */
     public void unregister() {
         synchronized (mLock) {
-            if (!isMagnifying()) {
-                unregisterInternalLocked();
-            } else {
-                mUnregisterPending = true;
-                resetLocked(true);
-            }
+            mScreenStateObserver.unregister();
         }
+        mDisplay.unregister();
     }
-
+    
     /**
      * Check if we are registered. Note that we may be planning to unregister at any moment.
      *
      * @return {@code true} if the controller is registered. {@code false} otherwise.
      */
     public boolean isRegisteredLocked() {
-        return mRegistered;
-    }
-
-    private void unregisterInternalLocked() {
-        if (mRegistered) {
-            mSpecAnimationBridge.setEnabled(false);
-            mScreenStateObserver.unregister();
-            mWindowManager.setMagnificationCallbacks(null);
-            mMagnificationRegion.setEmpty();
-            mRegistered = false;
-        }
-        mUnregisterPending = false;
+        return mDisplay.isRegisteredLocked();
     }
 
     /**
@@ -230,32 +675,7 @@
      *         is > 1, {@code false} otherwise
      */
     public boolean isMagnifying() {
-        return mCurrentMagnificationSpec.scale > 1.0f;
-    }
-
-    /**
-     * Update our copy of the current magnification region
-     *
-     * @param magnified the magnified region
-     */
-    private void onMagnificationRegionChanged(Region magnified) {
-        synchronized (mLock) {
-            if (!mRegistered) {
-                // Don't update if we've unregistered
-                return;
-            }
-            if (!mMagnificationRegion.equals(magnified)) {
-                mMagnificationRegion.set(magnified);
-                mMagnificationRegion.getBounds(mMagnificationBounds);
-                // It's possible that our magnification spec is invalid with the new bounds.
-                // Adjust the current spec's offsets if necessary.
-                if (updateCurrentSpecWithOffsetsLocked(
-                        mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) {
-                    sendSpecToAnimation(mCurrentMagnificationSpec, false);
-                }
-                onMagnificationChangedLocked();
-            }
-        }
+        return mDisplay.isMagnifying();
     }
 
     /**
@@ -268,9 +688,8 @@
      *         magnified region, or {@code false} otherwise
      */
     public boolean magnificationRegionContains(float x, float y) {
-        synchronized (mLock) {
-            return mMagnificationRegion.contains((int) x, (int) y);
-        }
+        return mDisplay.magnificationRegionContains(x, y);
+
     }
 
     /**
@@ -282,9 +701,7 @@
      *                  region
      */
     public void getMagnificationBounds(@NonNull Rect outBounds) {
-        synchronized (mLock) {
-            outBounds.set(mMagnificationBounds);
-        }
+        mDisplay.getMagnificationBounds(outBounds);
     }
 
     /**
@@ -295,9 +712,7 @@
      * @param outRegion the region to populate
      */
     public void getMagnificationRegion(@NonNull Region outRegion) {
-        synchronized (mLock) {
-            outRegion.set(mMagnificationRegion);
-        }
+        mDisplay.getMagnificationRegion(outRegion);
     }
 
     /**
@@ -307,7 +722,7 @@
      * @return the scale
      */
     public float getScale() {
-        return mCurrentMagnificationSpec.scale;
+        return mDisplay.getScale();
     }
 
     /**
@@ -317,7 +732,7 @@
      * @return the X offset
      */
     public float getOffsetX() {
-        return mCurrentMagnificationSpec.offsetX;
+        return mDisplay.getOffsetX();
     }
 
 
@@ -328,10 +743,7 @@
      * @return the X coordinate
      */
     public float getCenterX() {
-        synchronized (mLock) {
-            return (mMagnificationBounds.width() / 2.0f
-                    + mMagnificationBounds.left - getOffsetX()) / getScale();
-        }
+        return mDisplay.getCenterX();
     }
 
     /**
@@ -341,7 +753,7 @@
      * @return the Y offset
      */
     public float getOffsetY() {
-        return mCurrentMagnificationSpec.offsetY;
+        return mDisplay.getOffsetY();
     }
 
     /**
@@ -351,43 +763,7 @@
      * @return the Y coordinate
      */
     public float getCenterY() {
-        synchronized (mLock) {
-            return (mMagnificationBounds.height() / 2.0f
-                    + mMagnificationBounds.top - getOffsetY()) / getScale();
-        }
-    }
-
-    /**
-     * Returns the scale currently used by the window manager. If an
-     * animation is in progress, this reflects the current state of the
-     * animation.
-     *
-     * @return the scale currently used by the window manager
-     */
-    private float getSentScale() {
-        return mSpecAnimationBridge.mSentMagnificationSpec.scale;
-    }
-
-    /**
-     * Returns the X offset currently used by the window manager. If an
-     * animation is in progress, this reflects the current state of the
-     * animation.
-     *
-     * @return the X offset currently used by the window manager
-     */
-    private float getSentOffsetX() {
-        return mSpecAnimationBridge.mSentMagnificationSpec.offsetX;
-    }
-
-    /**
-     * Returns the Y offset currently used by the window manager. If an
-     * animation is in progress, this reflects the current state of the
-     * animation.
-     *
-     * @return the Y offset currently used by the window manager
-     */
-    private float getSentOffsetY() {
-        return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
+        return mDisplay.getCenterY();
     }
 
     /**
@@ -400,24 +776,9 @@
      *         the spec did not change
      */
     public boolean reset(boolean animate) {
-        synchronized (mLock) {
-            return resetLocked(animate);
-        }
-    }
 
-    private boolean resetLocked(boolean animate) {
-        if (!mRegistered) {
-            return false;
-        }
-        final MagnificationSpec spec = mCurrentMagnificationSpec;
-        final boolean changed = !spec.isNop();
-        if (changed) {
-            spec.clear();
-            onMagnificationChangedLocked();
-        }
-        mIdOfLastServiceToMagnify = INVALID_ID;
-        sendSpecToAnimation(spec, animate);
-        return changed;
+        return mDisplay.reset(animate);
+
     }
 
     /**
@@ -435,30 +796,8 @@
      *         the spec did not change
      */
     public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) {
-        synchronized (mLock) {
-            if (!mRegistered) {
-                return false;
-            }
-            // Constrain scale immediately for use in the pivot calculations.
-            scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-
-            final Rect viewport = mTempRect;
-            mMagnificationRegion.getBounds(viewport);
-            final MagnificationSpec spec = mCurrentMagnificationSpec;
-            final float oldScale = spec.scale;
-            final float oldCenterX
-                    = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
-            final float oldCenterY
-                    = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
-            final float normPivotX = (pivotX - spec.offsetX) / oldScale;
-            final float normPivotY = (pivotY - spec.offsetY) / oldScale;
-            final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
-            final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
-            final float centerX = normPivotX + offsetX;
-            final float centerY = normPivotY + offsetY;
-            mIdOfLastServiceToMagnify = id;
-            return setScaleAndCenterLocked(scale, centerX, centerY, animate, id);
-        }
+            return mDisplay.
+                    setScale(scale, pivotX, pivotY, animate, id);
     }
 
     /**
@@ -471,17 +810,13 @@
      *                center
      * @param animate {@code true} to animate the transition, {@code false}
      *                to transition immediately
-     * @param id the ID of the service requesting the change
+     * @param id      the ID of the service requesting the change
      * @return {@code true} if the magnification spec changed, {@code false} if
-     *         the spec did not change
+     * the spec did not change
      */
     public boolean setCenter(float centerX, float centerY, boolean animate, int id) {
-        synchronized (mLock) {
-            if (!mRegistered) {
-                return false;
-            }
-            return setScaleAndCenterLocked(Float.NaN, centerX, centerY, animate, id);
-        }
+            return mDisplay.
+                    setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
     }
 
     /**
@@ -502,28 +837,8 @@
      */
     public boolean setScaleAndCenter(
             float scale, float centerX, float centerY, boolean animate, int id) {
-        synchronized (mLock) {
-            if (!mRegistered) {
-                return false;
-            }
-            return setScaleAndCenterLocked(scale, centerX, centerY, animate, id);
-        }
-    }
-
-    private boolean setScaleAndCenterLocked(float scale, float centerX, float centerY,
-            boolean animate, int id) {
-        if (DEBUG) {
-            Slog.i(LOG_TAG,
-                    "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
-                            + ", centerY = " + centerY + ", animate = " + animate + ", id = " + id
-                            + ")");
-        }
-        final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
-        sendSpecToAnimation(mCurrentMagnificationSpec, animate);
-        if (isMagnifying() && (id != INVALID_ID)) {
-            mIdOfLastServiceToMagnify = id;
-        }
-        return changed;
+        return mDisplay.
+                setScaleAndCenter(scale, centerX, centerY, animate, id);
     }
 
     /**
@@ -531,27 +846,14 @@
      * opposite direction as the offsets passed in here.
      *
      * @param offsetX the amount in pixels to offset the region in the X direction, in current
-     * screen pixels.
+     *                screen pixels.
      * @param offsetY the amount in pixels to offset the region in the Y direction, in current
-     * screen pixels.
-     * @param id the ID of the service requesting the change
+     *                screen pixels.
+     * @param id      the ID of the service requesting the change
      */
     public void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
-        synchronized (mLock) {
-            if (!mRegistered) {
-                return;
-            }
-
-            final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
-            final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
-            if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
-                onMagnificationChangedLocked();
-            }
-            if (id != INVALID_ID) {
-                mIdOfLastServiceToMagnify = id;
-            }
-            sendSpecToAnimation(mCurrentMagnificationSpec, false);
-        }
+        mDisplay.offsetMagnifiedRegion(offsetX, offsetY,
+                id);
     }
 
     /**
@@ -560,28 +862,26 @@
      * @return The id
      */
     public int getIdOfLastServiceToMagnify() {
-        return mIdOfLastServiceToMagnify;
-    }
-
-    private void onMagnificationChangedLocked() {
-        mAms.notifyMagnificationChanged(mMagnificationRegion,
-                getScale(), getCenterX(), getCenterY());
-        if (mUnregisterPending && !isMagnifying()) {
-            unregisterInternalLocked();
-        }
+        return mDisplay.getIdOfLastServiceToMagnify();
     }
 
     /**
      * Persists the current magnification scale to the current user's settings.
      */
     public void persistScale() {
-        final float scale = mCurrentMagnificationSpec.scale;
+        persistScale(Display.DEFAULT_DISPLAY);
+    }
+    /**
+     * Persists the current magnification scale to the current user's settings.
+     */
+    public void persistScale(int displayId) {
+        final float scale = mDisplay.getScale();
         final int userId = mUserId;
 
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                mSettingsBridge.putMagnificationScale(scale, userId);
+                mSettingsBridge.putMagnificationScale(scale, displayId, userId);
                 return null;
             }
         }.execute();
@@ -595,98 +895,7 @@
      *         scale if none is available
      */
     public float getPersistedScale() {
-        return mSettingsBridge.getMagnificationScale(mUserId);
-    }
-
-    /**
-     * Updates the current magnification spec.
-     *
-     * @param scale the magnification scale
-     * @param centerX the unscaled, screen-relative X coordinate of the center
-     *                of the viewport, or {@link Float#NaN} to leave unchanged
-     * @param centerY the unscaled, screen-relative Y coordinate of the center
-     *                of the viewport, or {@link Float#NaN} to leave unchanged
-     * @return {@code true} if the magnification spec changed or {@code false}
-     *         otherwise
-     */
-    private boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
-        // Handle defaults.
-        if (Float.isNaN(centerX)) {
-            centerX = getCenterX();
-        }
-        if (Float.isNaN(centerY)) {
-            centerY = getCenterY();
-        }
-        if (Float.isNaN(scale)) {
-            scale = getScale();
-        }
-
-        // Compute changes.
-        boolean changed = false;
-
-        final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-        if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) {
-            mCurrentMagnificationSpec.scale = normScale;
-            changed = true;
-        }
-
-        final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f
-                + mMagnificationBounds.left - centerX * normScale;
-        final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f
-                + mMagnificationBounds.top - centerY * normScale;
-        changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY);
-
-        if (changed) {
-            onMagnificationChangedLocked();
-        }
-
-        return changed;
-    }
-
-    private boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
-        if (DEBUG) {
-            Slog.i(LOG_TAG,
-                    "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX
-                            + ", nonNormOffsetY = " + nonNormOffsetY + ")");
-        }
-        boolean changed = false;
-        final float offsetX = MathUtils.constrain(
-            nonNormOffsetX, getMinOffsetXLocked(), getMaxOffsetXLocked());
-        if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) {
-            mCurrentMagnificationSpec.offsetX = offsetX;
-            changed = true;
-        }
-        final float offsetY = MathUtils.constrain(
-            nonNormOffsetY, getMinOffsetYLocked(), getMaxOffsetYLocked());
-        if (Float.compare(mCurrentMagnificationSpec.offsetY, offsetY) != 0) {
-            mCurrentMagnificationSpec.offsetY = offsetY;
-            changed = true;
-        }
-        return changed;
-    }
-
-    private float getMinOffsetXLocked() {
-        final float viewportWidth = mMagnificationBounds.width();
-        final float viewportLeft = mMagnificationBounds.left;
-        return (viewportLeft + viewportWidth) -
-            (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale;
-    }
-
-    private float getMaxOffsetXLocked() {
-        return mMagnificationBounds.left -
-            mMagnificationBounds.left * mCurrentMagnificationSpec.scale;
-    }
-
-    private float getMinOffsetYLocked() {
-        final float viewportHeight = mMagnificationBounds.height();
-        final float viewportTop = mMagnificationBounds.top;
-        return (viewportTop + viewportHeight) -
-            (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale;
-    }
-
-    private float getMaxOffsetYLocked() {
-        return mMagnificationBounds.top -
-            mMagnificationBounds.top * mCurrentMagnificationSpec.scale;
+        return mSettingsBridge.getMagnificationScale(Display.DEFAULT_DISPLAY, mUserId);
     }
 
     /**
@@ -706,20 +915,14 @@
         }
     }
 
-    /**
+   /**
      * Resets magnification if magnification and auto-update are both enabled.
      *
      * @param animate whether the animate the transition
      * @return whether was {@link #isMagnifying magnifying}
      */
-    boolean resetIfNeeded(boolean animate) {
-        synchronized (mLock) {
-            if (isMagnifying()) {
-                reset(animate);
-                return true;
-            }
-            return false;
-        }
+    public boolean resetIfNeeded(boolean animate) {
+        return mDisplay.resetIfNeeded(animate);
     }
 
     /**
@@ -728,132 +931,23 @@
      * @param connectionId the connection ID be disabled.
      * @return {@code true} on success, {@code false} on failure
      */
-    boolean resetIfNeeded(int connectionId) {
-        if (mIdOfLastServiceToMagnify == connectionId) {
-            return resetIfNeeded(true /*animate*/);
-        }
-        return false;
+    public boolean resetIfNeeded(int connectionId) {
+        return mDisplay.resetIfNeeded(connectionId);
     }
 
     void setForceShowMagnifiableBounds(boolean show) {
-        if (mRegistered) {
-            mWindowManager.setForceShowMagnifiableBounds(show);
-        }
-    }
-
-    private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
-        final float scale = getSentScale();
-        final float offsetX = getSentOffsetX();
-        final float offsetY = getSentOffsetY();
-        getMagnificationBounds(outFrame);
-        outFrame.offset((int) -offsetX, (int) -offsetY);
-        outFrame.scale(1.0f / scale);
-    }
-
-    private void requestRectangleOnScreen(int left, int top, int right, int bottom) {
-        synchronized (mLock) {
-            final Rect magnifiedFrame = mTempRect;
-            getMagnificationBounds(magnifiedFrame);
-            if (!magnifiedFrame.intersects(left, top, right, bottom)) {
-                return;
-            }
-
-            final Rect magnifFrameInScreenCoords = mTempRect1;
-            getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords);
-
-            final float scrollX;
-            final float scrollY;
-            if (right - left > magnifFrameInScreenCoords.width()) {
-                final int direction = TextUtils
-                        .getLayoutDirectionFromLocale(Locale.getDefault());
-                if (direction == View.LAYOUT_DIRECTION_LTR) {
-                    scrollX = left - magnifFrameInScreenCoords.left;
-                } else {
-                    scrollX = right - magnifFrameInScreenCoords.right;
-                }
-            } else if (left < magnifFrameInScreenCoords.left) {
-                scrollX = left - magnifFrameInScreenCoords.left;
-            } else if (right > magnifFrameInScreenCoords.right) {
-                scrollX = right - magnifFrameInScreenCoords.right;
-            } else {
-                scrollX = 0;
-            }
-
-            if (bottom - top > magnifFrameInScreenCoords.height()) {
-                scrollY = top - magnifFrameInScreenCoords.top;
-            } else if (top < magnifFrameInScreenCoords.top) {
-                scrollY = top - magnifFrameInScreenCoords.top;
-            } else if (bottom > magnifFrameInScreenCoords.bottom) {
-                scrollY = bottom - magnifFrameInScreenCoords.bottom;
-            } else {
-                scrollY = 0;
-            }
-
-            final float scale = getScale();
-            offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID);
-        }
-    }
-
-    private void sendSpecToAnimation(MagnificationSpec spec, boolean animate) {
-        if (DEBUG) {
-            Slog.i(LOG_TAG, "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")");
-        }
-        if (Thread.currentThread().getId() == mMainThreadId) {
-            mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
-        } else {
-            mHandler.obtainMessage(MSG_SEND_SPEC_TO_ANIMATION,
-                    animate ? 1 : 0, 0, spec).sendToTarget();
-        }
+        mDisplay.setForceShowMagnifiableBounds(show);
     }
 
     private void onScreenTurnedOff() {
-        mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_OFF);
-    }
-
-    public boolean handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_SEND_SPEC_TO_ANIMATION:
-                final boolean animate = msg.arg1 == 1;
-                final MagnificationSpec spec = (MagnificationSpec) msg.obj;
-                mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
-                break;
-            case MSG_SCREEN_TURNED_OFF:
-                resetIfNeeded(false);
-                break;
-            case MSG_ON_MAGNIFIED_BOUNDS_CHANGED: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final Region magnifiedBounds = (Region) args.arg1;
-                onMagnificationRegionChanged(magnifiedBounds);
-                magnifiedBounds.recycle();
-                args.recycle();
-            } break;
-            case MSG_ON_RECTANGLE_ON_SCREEN_REQUESTED: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final int left = args.argi1;
-                final int top = args.argi2;
-                final int right = args.argi3;
-                final int bottom = args.argi4;
-                requestRectangleOnScreen(left, top, right, bottom);
-                args.recycle();
-            } break;
-            case MSG_ON_USER_CONTEXT_CHANGED:
-                resetIfNeeded(true);
-                break;
-        }
-        return true;
+        final Message m = PooledLambda.obtainMessage(
+                mDisplay::resetIfNeeded, false);
+        mHandler.sendMessage(m);
     }
 
     @Override
     public String toString() {
-        return "MagnificationController{" +
-                "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
-                ", mMagnificationRegion=" + mMagnificationRegion +
-                ", mMagnificationBounds=" + mMagnificationBounds +
-                ", mUserId=" + mUserId +
-                ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
-                ", mRegistered=" + mRegistered +
-                ", mUnregisterPending=" + mUnregisterPending +
-                '}';
+        return mDisplay.toString();
     }
 
     /**
@@ -974,6 +1068,7 @@
     private static class ScreenStateObserver extends BroadcastReceiver {
         private final Context mContext;
         private final MagnificationController mController;
+        private boolean mRegistered = false;
 
         public ScreenStateObserver(Context context, MagnificationController controller) {
             mContext = context;
@@ -981,11 +1076,17 @@
         }
 
         public void register() {
-            mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+            if (!mRegistered) {
+                mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+                mRegistered = true;
+            }
         }
 
         public void unregister() {
-            mContext.unregisterReceiver(this);
+            if (mRegistered) {
+                mContext.unregisterReceiver(this);
+                mRegistered = false;
+            }
         }
 
         @Override
@@ -1002,14 +1103,17 @@
             mContentResolver = contentResolver;
         }
 
-        public void putMagnificationScale(float value, int userId) {
+        public void putMagnificationScale(float value, int displayId, int userId) {
             Settings.Secure.putFloatForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE + (
+                            Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
+                    value, userId);
         }
 
-        public float getMagnificationScale(int userId) {
+        public float getMagnificationScale(int displayId, int userId) {
             return Settings.Secure.getFloatForUser(mContentResolver,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE
+                            + (Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
                     DEFAULT_MAGNIFICATION_SCALE, userId);
         }
     }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index da52d40..39866a7 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -56,6 +56,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -629,10 +630,10 @@
                     onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
                             providerUserId, true);
                 } else {
-                    final String dialogMessage = mPackageManagerInternal.getSuspendedDialogMessage(
-                            providerPackage, providerUserId);
+                    final SuspendDialogInfo dialogInfo = mPackageManagerInternal
+                            .getSuspendedDialogInfo(providerPackage, providerUserId);
                     onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
-                            providerPackage, suspendingPackage, dialogMessage, providerUserId);
+                            providerPackage, suspendingPackage, dialogInfo, providerUserId);
                 }
             } else if (provider.maskedByQuietProfile) {
                 showBadge = true;
diff --git a/services/art-profile b/services/art-profile
index 328f8f7..742ca1c 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -207,7 +207,7 @@
 HPLandroid/hardware/weaver/V1_0/IWeaver;->setHALInstrumentation()V
 HPLandroid/hardware/weaver/V1_0/IWeaver;->unlinkToDeath(Landroid/os/IHwBinder$DeathRecipient;)Z
 HPLandroid/hardware/weaver/V1_0/IWeaver;->write(ILjava/util/ArrayList;Ljava/util/ArrayList;)I
-HPLandroid/media/IMediaExtractorUpdateService;->loadPlugins(Ljava/lang/String;)V
+HPLandroid/media/IMediaUpdateService;->loadPlugins(Ljava/lang/String;)V
 HPLandroid/net/apf/ApfGenerator$Instruction;-><init>(Landroid/net/apf/ApfGenerator;Landroid/net/apf/ApfGenerator$Opcodes;Landroid/net/apf/ApfGenerator$Register;)V
 HPLandroid/net/apf/ApfGenerator$Instruction;->calculateImmSize(IZ)B
 HPLandroid/net/apf/ApfGenerator$Instruction;->calculateTargetLabelOffset()I
@@ -2082,6 +2082,22 @@
 HPLcom/android/server/usage/StorageStatsService;->queryStatsForUid(Ljava/lang/String;ILjava/lang/String;)Landroid/app/usage/StorageStats;
 HPLcom/android/server/usage/UnixCalendar;->getTimeInMillis()J
 HPLcom/android/server/usage/UsageStatsDatabase$CheckinAction;->checkin(Lcom/android/server/usage/IntervalStats;)Z
+HPLcom/android/server/usage/UsageStatsProto;->readStringPool(Landroid/util/proto/ProtoInputStream;)Ljava/util/List;
+HPLcom/android/server/usage/UsageStatsProto;->loadUsageStats(Landroid/util/proto/ProtoInputStream;Lcom/android/server/usage/IntervalStats;Ljava/util/List;)V
+HPLcom/android/server/usage/UsageStatsProto;->loadCountAndTime(Landroid/util/proto/ProtoInputStream;JLcom/android/server/usage/IntervalStats;$EventTracker)V
+HPLcom/android/server/usage/UsageStatsProto;->loadChooserCounts(Landroid/util/proto/ProtoInputStream;Landroid/app/usage/UsageStats;)V
+HPLcom/android/server/usage/UsageStatsProto;->loadCountsForAction(Landroid/util/proto/ProtoInputStream;Landroid/util/ArrayMap;)V
+HPLcom/android/server/usage/UsageStatsProto;->loadConfigStats(Landroid/util/proto/ProtoInputStream;JLcom/android/server/usage/IntervalStats;)V
+HPLcom/android/server/usage/UsageStatsProto;->loadEvent(Landroid/util/proto/ProtoInputStream;JLcom/android/server/usage/IntervalStats;Ljava/util/List;)V
+HPLcom/android/server/usage/UsageStatsProto;->writeStringPool(Landroid/util/proto/ProtoOutputStream;Lcom/android/server/usage/IntervalStats;)V
+HPLcom/android/server/usage/UsageStatsProto;->writeUsageStats(Landroid/util/proto/ProtoOutputStream;JLcom/android/server/usage/IntervalStats;Landroid/app/usage/UsageStats;)V
+HPLcom/android/server/usage/UsageStatsProto;->writeCountAndTime(Landroid/util/proto/ProtoOutputStream;JIJ)V
+HPLcom/android/server/usage/UsageStatsProto;->writeChooserCounts(Landroid/util/proto/ProtoOutputStream;Landroid/app/usage/UsageStats;)V
+HPLcom/android/server/usage/UsageStatsProto;->writeCountsForAction(Landroid/util/proto/ProtoOutputStream;Landroid/util/ArrayMap;)V
+HPLcom/android/server/usage/UsageStatsProto;->writeConfigStats(Landroid/util/proto/ProtoOutputStream;JLcom/android/server/usage/IntervalStats;Landroid/app/usage/ConfigurationStats;Z)V
+HPLcom/android/server/usage/UsageStatsProto;->writeEvent(Landroid/util/proto/ProtoOutputStream;JLcom/android/server/usage/IntervalStats;Landroid/app/usage/UsageEvents$Event;)V
+HPLcom/android/server/usage/UsageStatsProto;->read(Ljava/io/InputStream;Lcom/android/server/usage/IntervalStats;)V
+HPLcom/android/server/usage/UsageStatsProto;->write(Ljava/io/OutputStream;Lcom/android/server/usage/IntervalStats;)V
 HPLcom/android/server/usage/UsageStatsService$2;->onUidStateChanged(IIJ)V
 HPLcom/android/server/usage/UsageStatsService$BinderService;->hasPermission(Ljava/lang/String;)Z
 HPLcom/android/server/usage/UsageStatsService$BinderService;->isAppInactive(Ljava/lang/String;I)Z
@@ -3977,9 +3993,9 @@
 PLandroid/hardware/weaver/V1_0/WeaverReadResponse;-><init>()V
 PLandroid/hardware/weaver/V1_0/WeaverReadResponse;->readEmbeddedFromParcel(Landroid/os/HwParcel;Landroid/os/HwBlob;J)V
 PLandroid/hardware/weaver/V1_0/WeaverReadResponse;->readFromParcel(Landroid/os/HwParcel;)V
-PLandroid/media/IMediaExtractorUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-PLandroid/media/IMediaExtractorUpdateService$Stub$Proxy;->loadPlugins(Ljava/lang/String;)V
-PLandroid/media/IMediaExtractorUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaExtractorUpdateService;
+PLandroid/media/IMediaUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+PLandroid/media/IMediaUpdateService$Stub$Proxy;->loadPlugins(Ljava/lang/String;)V
+PLandroid/media/IMediaUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaExtractorUpdateService;
 PLandroid/net/apf/-$$Lambda$ApfFilter$UV1wDVoVlbcxpr8zevj_aMFtUGw;-><init>()V
 PLandroid/net/apf/-$$Lambda$ApfFilter$UV1wDVoVlbcxpr8zevj_aMFtUGw;->applyAsInt(Ljava/lang/Object;)I
 PLandroid/net/apf/ApfCapabilities;-><init>(III)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index af33bd0..d3842b7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -182,9 +182,7 @@
             final int userId = users.get(i).id;
             final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
             if (disabled) {
-                if (disabled) {
-                    Slog.i(TAG, "Disabling Autofill for user " + userId);
-                }
+                Slog.i(TAG, "Disabling Autofill for user " + userId);
                 mDisabledUsers.put(userId, disabled);
             }
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 78facf8..14d68cb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -185,23 +185,6 @@
         updateLocked(disabled);
     }
 
-    @Nullable
-    CharSequence getServiceName() {
-        final String packageName = getServicePackageName();
-        if (packageName == null) {
-            return null;
-        }
-
-        try {
-            final PackageManager pm = mContext.getPackageManager();
-            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
-            return pm.getApplicationLabel(info);
-        } catch (Exception e) {
-            Slog.e(TAG, "Could not get label for " + packageName + ": " + e);
-            return packageName;
-        }
-    }
-
     @GuardedBy("mLock")
     private int getServiceUidLocked() {
         if (mInfo == null) {
@@ -226,6 +209,7 @@
         return null;
     }
 
+    @Nullable
     ComponentName getServiceComponentName() {
         synchronized (mLock) {
             if (mInfo == null) {
@@ -706,17 +690,27 @@
         }
     }
 
-    @NonNull
-    CharSequence getServiceLabel() {
-        final CharSequence label = mInfo.getServiceInfo().loadSafeLabel(
+    /**
+     * Gets the user-visibile name of the service this service binds to, or {@code null} if the
+     * service is disabled.
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    public CharSequence getServiceLabelLocked() {
+        return mInfo == null ? null : mInfo.getServiceInfo().loadSafeLabel(
                 mContext.getPackageManager(), 0 /* do not ellipsize */,
                 PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
-        return label;
     }
 
+    /**
+     * Gets the icon of the service this service binds to, or {@code null} if the service is
+     * disabled.
+     */
     @NonNull
-    Drawable getServiceIcon() {
-        return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
+    @Nullable
+    @GuardedBy("mLock")
+    Drawable getServiceIconLocked() {
+        return mInfo == null ? null : mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
     }
 
     /**
@@ -959,7 +953,7 @@
         } else {
             pw.println();
             mInfo.dump(prefix2, pw);
-            pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel());
+            pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
             pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
         }
         pw.print(prefix); pw.print("Component from settings: ");
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c1b620c..f85749a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -48,6 +48,7 @@
 import android.content.IntentSender;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.os.Binder;
 import android.os.Build;
@@ -311,8 +312,8 @@
                                 Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
                                         + mUrlBar.getWebDomain());
                             }
-                            final ViewState viewState = new ViewState(Session.this, urlBarId,
-                                    Session.this, ViewState.STATE_URL_BAR);
+                            final ViewState viewState = new ViewState(urlBarId, Session.this,
+                                    ViewState.STATE_URL_BAR);
                             mViewStates.put(urlBarId, viewState);
                         }
                     }
@@ -1749,7 +1750,18 @@
 
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(mActivityToken, id, client);
-                getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
+
+                final CharSequence serviceLabel;
+                final Drawable serviceIcon;
+                synchronized (mLock) {
+                    serviceLabel = mService.getServiceLabelLocked();
+                    serviceIcon = mService.getServiceIconLocked();
+                }
+                if (serviceLabel == null || serviceIcon == null) {
+                    wtf(null, "showSaveLocked(): no service label or icon");
+                    return true;
+                }
+                getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
                         mService.getServicePackageName(), saveInfo, this,
                         mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode);
                 if (client != null) {
@@ -1801,8 +1813,6 @@
         return sanitizers;
     }
 
-    // TODO: this method is called a few times in the save process, we should cache its results into
-    // ViewState.
     @Nullable
     private AutofillValue getSanitizedValue(
             @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
@@ -1810,13 +1820,22 @@
             @Nullable AutofillValue value) {
         if (sanitizers == null || value == null) return value;
 
-        final InternalSanitizer sanitizer = sanitizers.get(id);
-        if (sanitizer == null) {
-            return value;
-        }
+        final ViewState state = mViewStates.get(id);
+        AutofillValue sanitized = state == null ? null : state.getSanitizedValue();
+        if (sanitized == null) {
+            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);
+            sanitized = sanitizer.sanitize(value);
+            if (sDebug) {
+                Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+            }
+            if (state != null) {
+                state.setSanitizedValue(sanitized);
+            }
+        }
         return sanitized;
     }
 
@@ -2137,7 +2156,7 @@
                     || action == ACTION_VIEW_ENTERED) {
                 if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
                 boolean isIgnored = isIgnoredLocked(id);
-                viewState = new ViewState(this, id, this,
+                viewState = new ViewState(id, this,
                         isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
                 mViewStates.put(id, viewState);
 
@@ -2318,9 +2337,19 @@
             filterText = value.getTextValue().toString();
         }
 
+        final CharSequence serviceLabel;
+        final Drawable serviceIcon;
+        synchronized (mLock) {
+            serviceLabel = mService.getServiceLabelLocked();
+            serviceIcon = mService.getServiceIconLocked();
+        }
+        if (serviceLabel == null || serviceIcon == null) {
+            wtf(null, "onFillReady(): no service label or icon");
+            return;
+        }
         getUiForShowing().showFillUi(filledId, response, filterText,
                 mService.getServicePackageName(), mComponentName,
-                mService.getServiceLabel(), mService.getServiceIcon(), this, id, mCompatMode);
+                serviceLabel, serviceIcon, this, id, mCompatMode);
 
         synchronized (mLock) {
             if (mUiShownTime == 0) {
@@ -2607,7 +2636,7 @@
         if (viewState != null)  {
             viewState.setState(state);
         } else {
-            viewState = new ViewState(this, id, this, state);
+            viewState = new ViewState(id, this, state);
             if (sVerbose) {
                 Slog.v(TAG, "Adding autofillable view with id " + id + " and state " + state);
             }
@@ -2655,12 +2684,6 @@
         }
     }
 
-    CharSequence getServiceName() {
-        synchronized (mLock) {
-            return mService.getServiceName();
-        }
-    }
-
     // TODO: this should never be null, but we got at least one occurrence, probably due to a race.
     @GuardedBy("mLock")
     @Nullable
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index a8dae03..2cc6d20 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -77,7 +77,6 @@
     public final AutofillId id;
 
     private final Listener mListener;
-    private final Session mSession;
 
     private FillResponse mResponse;
     private AutofillValue mCurrentValue;
@@ -87,8 +86,7 @@
     private int mState;
     private String mDatasetId;
 
-    ViewState(Session session, AutofillId id, Listener listener, int state) {
-        mSession = session;
+    ViewState(AutofillId id, Listener listener, int state) {
         this.id = id;
         mListener = listener;
         mState = state;
@@ -141,10 +139,6 @@
         mResponse = response;
     }
 
-    CharSequence getServiceName() {
-        return mSession.getServiceName();
-    }
-
     int getState() {
         return mState;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 1b97926..eb31e78 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -159,7 +159,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 
-public class BackupManagerService implements BackupManagerServiceInterface {
+public class BackupManagerService {
 
     public static final String TAG = "BackupManagerService";
     public static final boolean DEBUG = true;
@@ -701,7 +701,6 @@
     // Utility: build a new random integer token. The low bits are the ordinal of the
     // operation for near-time uniqueness, and the upper bits are random for app-
     // side unpredictability.
-    @Override
     public int generateRandomIntegerToken() {
         int token = mTokenGenerator.nextInt();
         if (token < 0) token = -token;
@@ -1108,12 +1107,10 @@
         return array;
     }
 
-    @Override
     public boolean setBackupPassword(String currentPw, String newPw) {
         return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
     }
 
-    @Override
     public boolean hasBackupPassword() {
         return mBackupPasswordManager.hasBackupPassword();
     }
@@ -1590,7 +1587,6 @@
 
     // Get the restore-set token for the best-available restore set for this package:
     // the active set if possible, else the ancestral one.  Returns zero if none available.
-    @Override
     public long getAvailableRestoreToken(String packageName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getAvailableRestoreToken");
@@ -1608,12 +1604,10 @@
         return token;
     }
 
-    @Override
     public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
         return requestBackup(packages, observer, null, flags);
     }
 
-    @Override
     public int requestBackup(String[] packages, IBackupObserver observer,
             IBackupManagerMonitor monitor, int flags) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
@@ -1702,7 +1696,6 @@
     }
 
     // Cancel all running backups.
-    @Override
     public void cancelBackups() {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
         if (MORE_DEBUG) {
@@ -1732,7 +1725,6 @@
         }
     }
 
-    @Override
     public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
             int operationType) {
         if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
@@ -1790,7 +1782,6 @@
     }
 
     // synchronous waiter case
-    @Override
     public boolean waitUntilOperationComplete(int token) {
         if (MORE_DEBUG) {
             Slog.i(TAG, "Blocking until operation complete for "
@@ -1895,7 +1886,6 @@
     }
 
 
-    @Override
     public void tearDownAgentAndKill(ApplicationInfo app) {
         if (app == null) {
             // Null means the system package, so just quietly move on.  :)
@@ -2049,7 +2039,6 @@
      * @return Whether ongoing work will continue.  The return value here will be passed
      * along as the return value to the scheduled job's onStartJob() callback.
      */
-    @Override
     public boolean beginFullBackup(FullBackupJob scheduledJob) {
         final long now = System.currentTimeMillis();
         final long fullBackupInterval;
@@ -2224,7 +2213,6 @@
 
     // The job scheduler says our constraints don't hold any more,
     // so tear down any ongoing backup task right away.
-    @Override
     public void endFullBackup() {
         // offload the mRunningFullBackupTask.handleCancel() call to another thread,
         // as we might have to wait for mCancelLock
@@ -2331,7 +2319,6 @@
 
     // ----- IBackupManager binder interface -----
 
-    @Override
     public void dataChanged(final String packageName) {
         final int callingUserHandle = UserHandle.getCallingUserId();
         if (callingUserHandle != UserHandle.USER_SYSTEM) {
@@ -2362,7 +2349,6 @@
     }
 
     // Run an initialize operation for the given transport
-    @Override
     public void initializeTransports(String[] transportNames, IBackupObserver observer) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                 "initializeTransport");
@@ -2382,7 +2368,6 @@
     }
 
     // Clear the given package's backup data from the current transport
-    @Override
     public void clearBackupData(String transportName, String packageName) {
         if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
         PackageInfo info;
@@ -2438,7 +2423,6 @@
 
     // Run a backup pass immediately for any applications that have declared
     // that they have pending updates.
-    @Override
     public void backupNow() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
 
@@ -2480,7 +2464,6 @@
     //
     // This is the variant used by 'adb backup'; it requires on-screen confirmation
     // by the user because it can be used to offload data over untrusted USB.
-    @Override
     public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
             boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
             boolean compress, boolean doKeyValue, String[] pkgList) {
@@ -2558,7 +2541,6 @@
         }
     }
 
-    @Override
     public void fullTransportBackup(String[] pkgNames) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                 "fullTransportBackup");
@@ -2618,7 +2600,6 @@
         }
     }
 
-    @Override
     public void adbRestore(ParcelFileDescriptor fd) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
 
@@ -2719,7 +2700,6 @@
 
     // Confirm that the previously-requested full backup/restore operation can proceed.  This
     // is used to require a user-facing disclosure about the operation.
-    @Override
     public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
             String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
         if (DEBUG) {
@@ -2819,7 +2799,6 @@
     }
 
     // Enable/disable backups
-    @Override
     public void setBackupEnabled(boolean enable) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "setBackupEnabled");
@@ -2887,7 +2866,6 @@
     }
 
     // Enable/disable automatic restore of app data at install time
-    @Override
     public void setAutoRestore(boolean doAutoRestore) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "setAutoRestore");
@@ -2907,7 +2885,6 @@
     }
 
     // Mark the backup service as having been provisioned
-    @Override
     public void setBackupProvisioned(boolean available) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "setBackupProvisioned");
@@ -2917,7 +2894,6 @@
     }
 
     // Report whether the backup mechanism is currently enabled
-    @Override
     public boolean isBackupEnabled() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "isBackupEnabled");
@@ -2925,7 +2901,6 @@
     }
 
     // Report the name of the currently active transport
-    @Override
     public String getCurrentTransport() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getCurrentTransport");
@@ -2938,7 +2913,6 @@
      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
      * null} if no transport selected or if the transport selected is not registered.
      */
-    @Override
     @Nullable
     public ComponentName getCurrentTransportComponent() {
         mContext.enforceCallingOrSelfPermission(
@@ -2954,7 +2928,6 @@
     }
 
     // Report all known, available backup transports
-    @Override
     public String[] listAllTransports() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "listAllTransports");
@@ -2962,14 +2935,12 @@
         return mTransportManager.getRegisteredTransportNames();
     }
 
-    @Override
     public ComponentName[] listAllTransportComponents() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "listAllTransportComponents");
         return mTransportManager.getRegisteredTransportComponents();
     }
 
-    @Override
     public String[] getTransportWhitelist() {
         // No permission check, intentionally.
         Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
@@ -3006,7 +2977,6 @@
      * @throws SecurityException If the UID of the calling process differs from the package UID of
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
      */
-    @Override
     public void updateTransportAttributes(
             ComponentName transportComponent,
             String name,
@@ -3070,7 +3040,6 @@
     }
 
     /** Selects transport {@code transportName} and returns previous selected transport. */
-    @Override
     @Deprecated
     @Nullable
     public String selectBackupTransport(String transportName) {
@@ -3089,7 +3058,6 @@
         }
     }
 
-    @Override
     public void selectBackupTransportAsync(
             ComponentName transportComponent, ISelectBackupTransportCallback listener) {
         mContext.enforceCallingOrSelfPermission(
@@ -3161,7 +3129,6 @@
     // Supply the configuration Intent for the given transport.  If the name is not one
     // of the available transports, or if the transport does not supply any configuration
     // UI, the method returns null.
-    @Override
     public Intent getConfigurationIntent(String transportName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getConfigurationIntent");
@@ -3186,7 +3153,6 @@
      * @param transportName The name of the registered transport.
      * @return The current destination string or null if the transport is not registered.
      */
-    @Override
     public String getDestinationString(String transportName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "getDestinationString");
@@ -3204,7 +3170,6 @@
     }
 
     // Supply the manage-data intent for the given transport.
-    @Override
     public Intent getDataManagementIntent(String transportName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDataManagementIntent");
@@ -3223,7 +3188,6 @@
 
     // Supply the menu label for affordances that fire the manage-data intent
     // for the given transport.
-    @Override
     public String getDataManagementLabel(String transportName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDataManagementLabel");
@@ -3242,7 +3206,6 @@
 
     // Callback: a requested backup agent has been instantiated.  This should only
     // be called from the Activity Manager.
-    @Override
     public void agentConnected(String packageName, IBinder agentBinder) {
         synchronized (mAgentConnectLock) {
             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
@@ -3261,7 +3224,6 @@
     // Callback: a backup agent has failed to come up, or has unexpectedly quit.
     // If the agent failed to come up in the first place, the agentBinder argument
     // will be null.  This should only be called from the Activity Manager.
-    @Override
     public void agentDisconnected(String packageName) {
         // TODO: handle backup being interrupted
         synchronized (mAgentConnectLock) {
@@ -3278,7 +3240,6 @@
 
     // An application being installed will need a restore pass, then the Package Manager
     // will need to be told when the restore is finished.
-    @Override
     public void restoreAtInstall(String packageName, int token) {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
@@ -3364,7 +3325,6 @@
     }
 
     // Hand off a restore session
-    @Override
     public IRestoreSession beginRestoreSession(String packageName, String transport) {
         if (DEBUG) {
             Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
@@ -3430,7 +3390,6 @@
 
     // Note that a currently-active backup agent has notified us that it has
     // completed the given outstanding asynchronous backup/restore operation.
-    @Override
     public void opComplete(int token, long result) {
         if (MORE_DEBUG) {
             Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
@@ -3468,7 +3427,6 @@
         }
     }
 
-    @Override
     public boolean isAppEligibleForBackup(String packageName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
@@ -3490,7 +3448,6 @@
         }
     }
 
-    @Override
     public String[] filterAppsEligibleForBackup(String[] packages) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
@@ -3517,7 +3474,6 @@
         }
     }
 
-    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
 
@@ -3667,7 +3623,6 @@
     }
 
 
-    @Override
     public IBackupManager getBackupManagerBinder() {
         return mBackupManagerBinder;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
deleted file mode 100644
index a38a0e9..0000000
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.backup;
-
-import android.annotation.Nullable;
-import android.app.IBackupAgent;
-import android.app.backup.IBackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.IRestoreSession;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Interface for BackupManagerService.
- *
- * Current and future implementations of BackupManagerService should use this interface, so that
- * Trampoline is able to switch between them.
- */
-public interface BackupManagerServiceInterface {
-
-  void unlockSystemUser();
-
-  // Utility: build a new random integer token
-  int generateRandomIntegerToken();
-
-  boolean setBackupPassword(String currentPw, String newPw);
-
-  boolean hasBackupPassword();
-
-  // Get the restore-set token for the best-available restore set for this package:
-  // the active set if possible, else the ancestral one.  Returns zero if none available.
-  long getAvailableRestoreToken(String packageName);
-
-  int requestBackup(String[] packages, IBackupObserver observer, int flags);
-
-  int requestBackup(String[] packages, IBackupObserver observer,
-      IBackupManagerMonitor monitor, int flags);
-
-  // Cancel all running backups.
-  void cancelBackups();
-
-  void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
-      int operationType);
-
-  // synchronous waiter case
-  boolean waitUntilOperationComplete(int token);
-
-  void tearDownAgentAndKill(ApplicationInfo app);
-
-  boolean beginFullBackup(FullBackupJob scheduledJob);
-
-  // The job scheduler says our constraints don't hold any more,
-  // so tear down any ongoing backup task right away.
-  void endFullBackup();
-
-  void dataChanged(String packageName);
-
-  // Initialize the given transport
-  void initializeTransports(String[] transportName, IBackupObserver observer);
-
-  // Clear the given package's backup data from the current transport
-  void clearBackupData(String transportName, String packageName);
-
-  // Run a backup pass immediately for any applications that have declared
-  // that they have pending updates.
-  void backupNow();
-
-  // Run a backup pass for the given packages, writing the resulting data stream
-  // to the supplied file descriptor.  This method is synchronous and does not return
-  // to the caller until the backup has been completed.
-  //
-  // This is the variant used by 'adb backup'; it requires on-screen confirmation
-  // by the user because it can be used to offload data over untrusted USB.
-  void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-      boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
-      boolean compress, boolean doKeyValue, String[] pkgList);
-
-  void fullTransportBackup(String[] pkgNames);
-
-  void adbRestore(ParcelFileDescriptor fd);
-
-  // Confirm that the previously-requested full backup/restore operation can proceed.  This
-  // is used to require a user-facing disclosure about the operation.
-  void acknowledgeAdbBackupOrRestore(int token, boolean allow,
-      String curPassword, String encPpassword, IFullBackupRestoreObserver observer);
-
-  // Enable/disable backups
-  void setBackupEnabled(boolean enable);
-
-  // Enable/disable automatic restore of app data at install time
-  void setAutoRestore(boolean doAutoRestore);
-
-  // Mark the backup service as having been provisioned
-  void setBackupProvisioned(boolean available);
-
-  // Report whether the backup mechanism is currently enabled
-  boolean isBackupEnabled();
-
-  // Update the transport attributes
-  void updateTransportAttributes(
-          ComponentName transportComponent,
-          String name,
-          Intent configurationIntent,
-          String currentDestinationString,
-          Intent dataManagementIntent,
-          String dataManagementLabel);
-
-  // Report the name of the currently active transport
-  String getCurrentTransport();
-
-  // Report the component name of the host service of the currently active transport
-  @Nullable
-  ComponentName getCurrentTransportComponent();
-
-  // Report all known, available backup transports
-  String[] listAllTransports();
-
-  ComponentName[] listAllTransportComponents();
-
-  String[] getTransportWhitelist();
-
-  // Select which transport to use for the next backup operation.
-  String selectBackupTransport(String transport);
-
-  void selectBackupTransportAsync(ComponentName transport,
-      ISelectBackupTransportCallback listener);
-
-  // Supply the configuration Intent for the given transport.  If the name is not one
-  // of the available transports, or if the transport does not supply any configuration
-  // UI, the method returns null.
-  Intent getConfigurationIntent(String transportName);
-
-  // Supply the configuration summary string for the given transport.  If the name is
-  // not one of the available transports, or if the transport does not supply any
-  // summary / destination string, the method can return null.
-  //
-  // This string is used VERBATIM as the summary text of the relevant Settings item!
-  String getDestinationString(String transportName);
-
-  // Supply the manage-data intent for the given transport.
-  Intent getDataManagementIntent(String transportName);
-
-  // Supply the menu label for affordances that fire the manage-data intent
-  // for the given transport.
-  String getDataManagementLabel(String transportName);
-
-  // Callback: a requested backup agent has been instantiated.  This should only
-  // be called from the Activity Manager.
-  void agentConnected(String packageName, IBinder agentBinder);
-
-  // Callback: a backup agent has failed to come up, or has unexpectedly quit.
-  // If the agent failed to come up in the first place, the agentBinder argument
-  // will be null.  This should only be called from the Activity Manager.
-  void agentDisconnected(String packageName);
-
-  // An application being installed will need a restore pass, then the Package Manager
-  // will need to be told when the restore is finished.
-  void restoreAtInstall(String packageName, int token);
-
-  // Hand off a restore session
-  IRestoreSession beginRestoreSession(String packageName, String transport);
-
-  // Note that a currently-active backup agent has notified us that it has
-  // completed the given outstanding asynchronous backup/restore operation.
-  void opComplete(int token, long result);
-
-  boolean isAppEligibleForBackup(String packageName);
-
-  String[] filterAppsEligibleForBackup(String[] packages);
-
-  void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
-  IBackupManager getBackupManagerBinder();
-
-  // Gets access to the backup/restore agent timeout parameters.
-  BackupAgentTimeoutParameters getAgentTimeoutParameters();
-}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index fbec5cb..bb14576 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -39,7 +39,7 @@
     private static final String TAG = "KeyValueAdbRestoreEngine";
     private static final boolean DEBUG = false;
 
-    private final BackupManagerServiceInterface mBackupManagerService;
+    private final BackupManagerService mBackupManagerService;
     private final File mDataDir;
 
     FileMetadata mInfo;
@@ -48,7 +48,7 @@
     IBackupAgent mAgent;
     int mToken;
 
-    public KeyValueAdbRestoreEngine(BackupManagerServiceInterface backupManagerService,
+    public KeyValueAdbRestoreEngine(BackupManagerService backupManagerService,
             File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
             int token) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 787d667..818154b 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -76,7 +76,7 @@
     final Context mContext;
     final File mSuppressFile;   // existence testing & creating synchronized on 'this'
     final boolean mGlobalDisable;
-    volatile BackupManagerServiceInterface mService;
+    volatile BackupManagerService mService;
 
     private HandlerThread mHandlerThread;
 
@@ -100,7 +100,7 @@
                 BACKUP_SUPPRESS_FILENAME);
     }
 
-    protected BackupManagerServiceInterface createBackupManagerService() {
+    protected BackupManagerService createBackupManagerService() {
         return BackupManagerService.create(mContext, this, mHandlerThread);
     }
 
@@ -135,7 +135,7 @@
             initialize(UserHandle.USER_SYSTEM);
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
-            BackupManagerServiceInterface svc = mService;
+            BackupManagerService svc = mService;
             Slog.i(TAG, "Unlocking system user; mService=" + mService);
             if (svc != null) {
                 svc.unlockSystemUser();
@@ -198,7 +198,7 @@
 
     @Override
     public void dataChanged(String packageName) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.dataChanged(packageName);
         }
@@ -207,7 +207,7 @@
     @Override
     public void initializeTransports(String[] transportNames, IBackupObserver observer)
             throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.initializeTransports(transportNames, observer);
         }
@@ -216,7 +216,7 @@
     @Override
     public void clearBackupData(String transportName, String packageName)
             throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.clearBackupData(transportName, packageName);
         }
@@ -224,7 +224,7 @@
 
     @Override
     public void agentConnected(String packageName, IBinder agent) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.agentConnected(packageName, agent);
         }
@@ -232,7 +232,7 @@
 
     @Override
     public void agentDisconnected(String packageName) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.agentDisconnected(packageName);
         }
@@ -240,7 +240,7 @@
 
     @Override
     public void restoreAtInstall(String packageName, int token) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.restoreAtInstall(packageName, token);
         }
@@ -248,7 +248,7 @@
 
     @Override
     public void setBackupEnabled(boolean isEnabled) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.setBackupEnabled(isEnabled);
         }
@@ -256,7 +256,7 @@
 
     @Override
     public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.setAutoRestore(doAutoRestore);
         }
@@ -264,7 +264,7 @@
 
     @Override
     public void setBackupProvisioned(boolean isProvisioned) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.setBackupProvisioned(isProvisioned);
         }
@@ -272,25 +272,25 @@
 
     @Override
     public boolean isBackupEnabled() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.isBackupEnabled() : false;
     }
 
     @Override
     public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.setBackupPassword(currentPw, newPw) : false;
     }
 
     @Override
     public boolean hasBackupPassword() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.hasBackupPassword() : false;
     }
 
     @Override
     public void backupNow() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.backupNow();
         }
@@ -301,7 +301,7 @@
             boolean includeShared, boolean doWidgets, boolean allApps,
             boolean allIncludesSystem, boolean doCompress, boolean doKeyValue, String[] packageNames)
                     throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.adbBackup(fd, includeApks, includeObbs, includeShared, doWidgets,
                     allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
@@ -310,7 +310,7 @@
 
     @Override
     public void fullTransportBackup(String[] packageNames) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.fullTransportBackup(packageNames);
         }
@@ -318,7 +318,7 @@
 
     @Override
     public void adbRestore(ParcelFileDescriptor fd) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.adbRestore(fd);
         }
@@ -328,7 +328,7 @@
     public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
             String encryptionPassword, IFullBackupRestoreObserver observer)
                     throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.acknowledgeAdbBackupOrRestore(token, allow,
                     curPassword, encryptionPassword, observer);
@@ -337,7 +337,7 @@
 
     @Override
     public String getCurrentTransport() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getCurrentTransport() : null;
     }
 
@@ -348,25 +348,25 @@
     @Override
     @Nullable
     public ComponentName getCurrentTransportComponent() {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getCurrentTransportComponent() : null;
     }
 
     @Override
     public String[] listAllTransports() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.listAllTransports() : null;
     }
 
     @Override
     public ComponentName[] listAllTransportComponents() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.listAllTransportComponents() : null;
     }
 
     @Override
     public String[] getTransportWhitelist() {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getTransportWhitelist() : null;
     }
 
@@ -378,7 +378,7 @@
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
             String dataManagementLabel) {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.updateTransportAttributes(
                     transportComponent,
@@ -392,14 +392,14 @@
 
     @Override
     public String selectBackupTransport(String transport) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.selectBackupTransport(transport) : null;
     }
 
     @Override
     public void selectBackupTransportAsync(ComponentName transport,
             ISelectBackupTransportCallback listener) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.selectBackupTransportAsync(transport, listener);
         } else {
@@ -415,38 +415,38 @@
 
     @Override
     public Intent getConfigurationIntent(String transport) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getConfigurationIntent(transport) : null;
     }
 
     @Override
     public String getDestinationString(String transport) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getDestinationString(transport) : null;
     }
 
     @Override
     public Intent getDataManagementIntent(String transport) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getDataManagementIntent(transport) : null;
     }
 
     @Override
     public String getDataManagementLabel(String transport) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getDataManagementLabel(transport) : null;
     }
 
     @Override
     public IRestoreSession beginRestoreSession(String packageName, String transportID)
             throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.beginRestoreSession(packageName, transportID) : null;
     }
 
     @Override
     public void opComplete(int token, long result) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.opComplete(token, result);
         }
@@ -454,26 +454,26 @@
 
     @Override
     public long getAvailableRestoreToken(String packageName) {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.getAvailableRestoreToken(packageName) : 0;
     }
 
     @Override
     public boolean isAppEligibleForBackup(String packageName) {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.isAppEligibleForBackup(packageName) : false;
     }
 
     @Override
     public String[] filterAppsEligibleForBackup(String[] packages) {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.filterAppsEligibleForBackup(packages) : null;
     }
 
     @Override
     public int requestBackup(String[] packages, IBackupObserver observer,
             IBackupManagerMonitor monitor, int flags) throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc == null) {
             return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
         }
@@ -482,7 +482,7 @@
 
     @Override
     public void cancelBackups() throws RemoteException {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.cancelBackups();
         }
@@ -492,7 +492,7 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.dump(fd, pw, args);
         } else {
@@ -503,12 +503,12 @@
     // Full backup/restore entry points - non-Binder; called directly
     // by the full-backup scheduled job
     /* package */ boolean beginFullBackup(FullBackupJob scheduledJob) {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         return (svc != null) ? svc.beginFullBackup(scheduledJob) : false;
     }
 
     /* package */ void endFullBackup() {
-        BackupManagerServiceInterface svc = mService;
+        BackupManagerService svc = mService;
         if (svc != null) {
             svc.endFullBackup();
         }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index af33cbc..38b9647 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -285,14 +285,21 @@
     @VisibleForTesting
     final class Constants extends ContentObserver {
         // Key names stored in the settings value.
-        private static final String KEY_MIN_FUTURITY = "min_futurity";
-        private static final String KEY_MIN_INTERVAL = "min_interval";
-        private static final String KEY_MAX_INTERVAL = "max_interval";
-        private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
-        private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
-        private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
+        @VisibleForTesting
+        static final String KEY_MIN_FUTURITY = "min_futurity";
+        @VisibleForTesting
+        static final String KEY_MIN_INTERVAL = "min_interval";
+        @VisibleForTesting
+        static final String KEY_MAX_INTERVAL = "max_interval";
+        @VisibleForTesting
+        static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
+        @VisibleForTesting
+        static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
+        @VisibleForTesting
+        static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
                 = "allow_while_idle_whitelist_duration";
-        private static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
+        @VisibleForTesting
+        static final String KEY_LISTENER_TIMEOUT = "listener_timeout";
 
         // Keys for specifying throttling delay based on app standby bucketing
         private final String[] KEYS_APP_STANDBY_DELAY = {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bc6254a..1c8d99a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,8 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
@@ -189,6 +191,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Comparator;
+import java.util.ConcurrentModificationException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -257,6 +260,14 @@
     @GuardedBy("mVpns")
     private LockdownVpnTracker mLockdownTracker;
 
+    /**
+     * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
+     * handler thread, they don't need a lock.
+     */
+    private SparseIntArray mUidRules = new SparseIntArray();
+    /** Flag indicating if background data is restricted. */
+    private boolean mRestrictBackground;
+
     final private Context mContext;
     // 0 is full bad, 100 is full good
     private int mDefaultInetConditionPublished = 0;
@@ -419,6 +430,16 @@
     // Handle private DNS validation status updates.
     private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
 
+    /**
+     * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
+     */
+    private static final int EVENT_UID_RULES_CHANGED = 39;
+
+    /**
+     * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
+     */
+    private static final int EVENT_DATA_SAVER_CHANGED = 40;
+
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
@@ -780,6 +801,9 @@
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
+        // To ensure uid rules are synchronized with Network Policy, register for
+        // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
+        // reading existing policy from disk.
         try {
             mPolicyManager.registerListener(mPolicyListener);
         } catch (RemoteException e) {
@@ -910,7 +934,8 @@
         registerPrivateDnsSettingsCallbacks();
     }
 
-    private Tethering makeTethering() {
+    @VisibleForTesting
+    protected Tethering makeTethering() {
         // TODO: Move other elements into @Overridden getters.
         final TetheringDependencies deps = new TetheringDependencies() {
             @Override
@@ -1116,11 +1141,6 @@
         if (ignoreBlocked) {
             return false;
         }
-        // Networks are never blocked for system services
-        // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked.
-        if (isSystem(uid)) {
-            return false;
-        }
         synchronized (mVpns) {
             final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
             if (vpn != null && vpn.isBlockingUid(uid)) {
@@ -1150,6 +1170,17 @@
         mNetworkInfoBlockingLogs.log(action + " " + uid);
     }
 
+    private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
+            boolean blocked) {
+        if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
+            return;
+        }
+        String action = blocked ? "BLOCKED" : "UNBLOCKED";
+        log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
+                nri.mUid, nri.request.requestId, net.netId));
+        mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+    }
+
     /**
      * Apply any relevant filters to {@link NetworkState} for the given UID. For
      * example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1651,10 +1682,17 @@
     private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
-            // TODO: notify UID when it has requested targeted updates
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
         }
         @Override
         public void onRestrictBackgroundChanged(boolean restrictBackground) {
+            // caller is NPMS, since we only register with them
+            if (LOGD_BLOCKED_NETWORKINFO) {
+                log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+            }
+            mHandler.sendMessage(mHandler.obtainMessage(
+                    EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+
             // TODO: relocate this specific callback in Tethering.
             if (restrictBackground) {
                 log("onRestrictBackgroundChanged(true): disabling tethering");
@@ -1663,6 +1701,50 @@
         }
     };
 
+    void handleUidRulesChanged(int uid, int newRules) {
+        // skip update when we've already applied rules
+        final int oldRules = mUidRules.get(uid, RULE_NONE);
+        if (oldRules == newRules) return;
+
+        maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
+
+        if (newRules == RULE_NONE) {
+            mUidRules.delete(uid);
+        } else {
+            mUidRules.put(uid, newRules);
+        }
+    }
+
+    void handleRestrictBackgroundChanged(boolean restrictBackground) {
+        if (mRestrictBackground == restrictBackground) return;
+
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            final boolean curMetered = nai.networkCapabilities.isMetered();
+            maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+                    restrictBackground);
+        }
+
+        mRestrictBackground = restrictBackground;
+    }
+
+    private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted) {
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+            // Because the return value of this function depends on the list of UIDs the
+            // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
+            // list all state depending on the return value of this function has to be recomputed.
+            // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
+            // send the necessary onBlockedStatusChanged callbacks.
+            if (vpn != null && vpn.isBlockingUid(uid)) {
+                return true;
+            }
+        }
+
+        return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+                isNetworkMetered, isBackgroundRestricted);
+    }
+
     /**
      * Require that the caller is either in the same user or has appropriate permission to interact
      * across users.
@@ -2118,6 +2200,28 @@
         pw.decreaseIndent();
         pw.println();
 
+        pw.print("Restrict background: ");
+        pw.println(mRestrictBackground);
+        pw.println();
+
+        pw.println("Status for known UIDs:");
+        pw.increaseIndent();
+        final int size = mUidRules.size();
+        for (int i = 0; i < size; i++) {
+            // Don't crash if the array is modified while dumping in bugreports.
+            try {
+                final int uid = mUidRules.keyAt(i);
+                final int uidRules = mUidRules.get(uid, RULE_NONE);
+                pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+            } catch (ArrayIndexOutOfBoundsException e) {
+                pw.println("  ArrayIndexOutOfBoundsException");
+            } catch (ConcurrentModificationException e) {
+                pw.println("  ConcurrentModificationException");
+            }
+        }
+        pw.println();
+        pw.decreaseIndent();
+
         pw.println("Network Requests:");
         pw.increaseIndent();
         dumpNetworkRequests(pw);
@@ -3195,6 +3299,12 @@
                     handlePrivateDnsValidationUpdate(
                             (PrivateDnsValidationUpdate) msg.obj);
                     break;
+                case EVENT_UID_RULES_CHANGED:
+                    handleUidRulesChanged(msg.arg1, msg.arg2);
+                    break;
+                case EVENT_DATA_SAVER_CHANGED:
+                    handleRestrictBackgroundChanged(toBool(msg.arg1));
+                    break;
             }
         }
     }
@@ -3495,7 +3605,7 @@
         ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
 
         if (!ProxyTracker.proxyInfoEqual(newProxyInfo, oldProxyInfo)) {
-            mProxyTracker.sendProxyBroadcast(mProxyTracker.getDefaultProxy());
+            mProxyTracker.sendProxyBroadcast();
         }
     }
 
@@ -3783,6 +3893,8 @@
     private void setLockdownTracker(LockdownVpnTracker tracker) {
         // Shutdown any existing tracker
         final LockdownVpnTracker existing = mLockdownTracker;
+        // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+        // necessary onBlockedStatusChanged callbacks.
         mLockdownTracker = null;
         if (existing != null) {
             existing.shutdown();
@@ -4782,15 +4894,14 @@
         }
     }
 
-    private String getNetworkPermission(NetworkCapabilities nc) {
-        // TODO: make these permission strings AIDL constants instead.
+    private int getNetworkPermission(NetworkCapabilities nc) {
         if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
-            return NetworkManagementService.PERMISSION_SYSTEM;
+            return INetd.PERMISSION_SYSTEM;
         }
         if (!nc.hasCapability(NET_CAPABILITY_FOREGROUND)) {
-            return NetworkManagementService.PERMISSION_NETWORK;
+            return INetd.PERMISSION_NETWORK;
         }
-        return null;
+        return INetd.PERMISSION_NONE;
     }
 
     /**
@@ -4863,9 +4974,9 @@
 
         if (Objects.equals(nai.networkCapabilities, newNc)) return;
 
-        final String oldPermission = getNetworkPermission(nai.networkCapabilities);
-        final String newPermission = getNetworkPermission(newNc);
-        if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
+        final int oldPermission = getNetworkPermission(nai.networkCapabilities);
+        final int newPermission = getNetworkPermission(newNc);
+        if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
             try {
                 mNMS.setNetworkPermission(nai.network.netId, newPermission);
             } catch (RemoteException e) {
@@ -4894,12 +5005,20 @@
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
 
-        // Report changes that are interesting for network statistics tracking.
         if (prevNc != null) {
-            final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
-                    newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
+            final boolean oldMetered = prevNc.isMetered();
+            final boolean newMetered = newNc.isMetered();
+            final boolean meteredChanged = oldMetered != newMetered;
+
+            if (meteredChanged) {
+                maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
+                        mRestrictBackground);
+            }
+
             final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
                     newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+            // Report changes that are interesting for network statistics tracking.
             if (meteredChanged || roamingChanged) {
                 notifyIfacesChangedForNetworkStats();
             }
@@ -5029,6 +5148,8 @@
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+                // For this notification, arg1 contains the blocked status.
+                msg.arg1 = arg1;
                 break;
             }
             case ConnectivityManager.CALLBACK_LOSING: {
@@ -5046,6 +5167,10 @@
                 putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
                 break;
             }
+            case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+                msg.arg1 = arg1;
+                break;
+            }
         }
         msg.what = notificationType;
         msg.setData(bundle);
@@ -5601,7 +5726,76 @@
             return;
         }
 
-        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+        final boolean metered = nai.networkCapabilities.isMetered();
+        final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+                metered, mRestrictBackground);
+        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+    }
+
+    /**
+     * Notify of the blocked state apps with a registered callback matching a given NAI.
+     *
+     * Unlike other callbacks, blocked status is different between each individual uid. So for
+     * any given nai, all requests need to be considered according to the uid who filed it.
+     *
+     * @param nai The target NetworkAgentInfo.
+     * @param oldMetered True if the previous network capabilities is metered.
+     * @param newRestrictBackground True if data saver is enabled.
+     */
+    private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
+            boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+
+        for (int i = 0; i < nai.numNetworkRequests(); i++) {
+            NetworkRequest nr = nai.requestAt(i);
+            NetworkRequestInfo nri = mNetworkRequests.get(nr);
+            final int uidRules = mUidRules.get(nri.mUid);
+            final boolean oldBlocked, newBlocked;
+            // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
+            // between these two calls.
+            synchronized (mVpns) {
+                oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
+                        oldRestrictBackground);
+                newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
+                        newRestrictBackground);
+            }
+            if (oldBlocked != newBlocked) {
+                callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+                        encodeBool(newBlocked));
+            }
+        }
+    }
+
+    /**
+     * Notify apps with a given UID of the new blocked state according to new uid rules.
+     * @param uid The uid for which the rules changed.
+     * @param newRules The new rules to apply.
+     */
+    private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            final boolean metered = nai.networkCapabilities.isMetered();
+            final boolean oldBlocked, newBlocked;
+            // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
+            // rules changed event. And this function actually loop through all connected nai and
+            // its requests. It seems that mVpns lock will be grabbed frequently in this case.
+            // Reduce the number of locking or optimize the use of lock are likely needed in future.
+            synchronized (mVpns) {
+                oldBlocked = isUidNetworkingWithVpnBlocked(
+                        uid, mUidRules.get(uid), metered, mRestrictBackground);
+                newBlocked = isUidNetworkingWithVpnBlocked(
+                        uid, newRules, metered, mRestrictBackground);
+            }
+            if (oldBlocked == newBlocked) {
+                return;
+            }
+            final int arg = encodeBool(newBlocked);
+            for (int i = 0; i < nai.numNetworkRequests(); i++) {
+                NetworkRequest nr = nai.requestAt(i);
+                NetworkRequestInfo nri = mNetworkRequests.get(nr);
+                if (nri != null && nri.mUid == uid) {
+                    callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+                }
+            }
+        }
     }
 
     private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 26421a2..af9d4c8 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -54,6 +54,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -116,6 +117,11 @@
 
        STATE_ACTIVE [label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"]
        STATE_INACTIVE [label="STATE_INACTIVE\nScreen off AND Not charging"]
+       STATE_QUICK_DOZE_DELAY [
+         label="STATE_QUICK_DOZE_DELAY\n"
+             + "Screen off AND Not charging\n"
+             + "Location, motion detection, and significant motion monitoring turned off"
+       ]
        STATE_IDLE_PENDING [
          label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on"
        ]
@@ -125,26 +131,40 @@
        ]
        STATE_IDLE [
          label="STATE_IDLE\nLocation and motion detection turned off\n"
-             + "Significant motion monitoring still on"
+             + "Significant motion monitoring state unchanged"
        ]
        STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n"]
 
-       STATE_ACTIVE -> STATE_INACTIVE [label="becomeInactiveIfAppropriateLocked()"]
+       STATE_ACTIVE -> STATE_INACTIVE [
+         label="becomeInactiveIfAppropriateLocked() AND Quick Doze not enabled"
+       ]
+       STATE_ACTIVE -> STATE_QUICK_DOZE_DELAY [
+         label="becomeInactiveIfAppropriateLocked() AND Quick Doze enabled"
+       ]
 
        STATE_INACTIVE -> STATE_ACTIVE [
          label="handleMotionDetectedLocked(), becomeActiveLocked()"
        ]
        STATE_INACTIVE -> STATE_IDLE_PENDING [label="stepIdleStateLocked()"]
+       STATE_INACTIVE -> STATE_QUICK_DOZE_DELAY [
+         label="becomeInactiveIfAppropriateLocked() AND Quick Doze enabled"
+       ]
 
        STATE_IDLE_PENDING -> STATE_ACTIVE [
          label="handleMotionDetectedLocked(), becomeActiveLocked()"
        ]
        STATE_IDLE_PENDING -> STATE_SENSING [label="stepIdleStateLocked()"]
+       STATE_IDLE_PENDING -> STATE_QUICK_DOZE_DELAY [
+         label="becomeInactiveIfAppropriateLocked() AND Quick Doze enabled"
+       ]
 
        STATE_SENSING -> STATE_ACTIVE [
          label="handleMotionDetectedLocked(), becomeActiveLocked()"
        ]
        STATE_SENSING -> STATE_LOCATING [label="stepIdleStateLocked()"]
+       STATE_SENSING -> STATE_QUICK_DOZE_DELAY [
+         label="becomeInactiveIfAppropriateLocked() AND Quick Doze enabled"
+       ]
        STATE_SENSING -> STATE_IDLE [
          label="stepIdleStateLocked()\n"
              + "No Location Manager OR (no Network provider AND no GPS provider)"
@@ -153,8 +173,16 @@
        STATE_LOCATING -> STATE_ACTIVE [
          label="handleMotionDetectedLocked(), becomeActiveLocked()"
        ]
+       STATE_LOCATING -> STATE_QUICK_DOZE_DELAY [
+         label="becomeInactiveIfAppropriateLocked() AND Quick Doze enabled"
+       ]
        STATE_LOCATING -> STATE_IDLE [label="stepIdleStateLocked()"]
 
+       STATE_QUICK_DOZE_DELAY -> STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+       STATE_QUICK_DOZE_DELAY -> STATE_IDLE [label="stepIdleStateLocked()"]
+
        STATE_IDLE -> STATE_ACTIVE [label="handleMotionDetectedLocked(), becomeActiveLocked()"]
        STATE_IDLE -> STATE_IDLE_MAINTENANCE [label="stepIdleStateLocked()"]
 
@@ -242,11 +270,9 @@
     private ActivityTaskManagerInternal mLocalActivityTaskManager;
     private PowerManagerInternal mLocalPowerManager;
     private PowerManager mPowerManager;
-    private ConnectivityService mConnectivityService;
     private INetworkPolicyManager mNetworkPolicyManager;
     private SensorManager mSensorManager;
     private Sensor mMotionSensor;
-    private LocationManager mLocationManager;
     private LocationRequest mLocationRequest;
     private Intent mIdleIntent;
     private Intent mLightIdleIntent;
@@ -254,6 +280,7 @@
     private final AppStateTracker mAppStateTracker;
     private boolean mLightEnabled;
     private boolean mDeepEnabled;
+    private boolean mQuickDozeActivated;
     private boolean mForceIdle;
     private boolean mNetworkConnected;
     private boolean mScreenOn;
@@ -289,6 +316,12 @@
     /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
     @VisibleForTesting
     static final int STATE_IDLE_MAINTENANCE = 6;
+    /**
+     * Device is inactive and should go straight into idle (foregoing motion and location
+     * monitoring), but allow some time for current work to complete first.
+     */
+    @VisibleForTesting
+    static final int STATE_QUICK_DOZE_DELAY = 7;
 
     @VisibleForTesting
     static String stateToString(int state) {
@@ -300,6 +333,7 @@
             case STATE_LOCATING: return "LOCATING";
             case STATE_IDLE: return "IDLE";
             case STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
+            case STATE_QUICK_DOZE_DELAY: return "QUICK_DOZE_DELAY";
             default: return Integer.toString(state);
         }
     }
@@ -690,6 +724,7 @@
         private static final String KEY_IDLE_PENDING_TIMEOUT = "idle_pending_to";
         private static final String KEY_MAX_IDLE_PENDING_TIMEOUT = "max_idle_pending_to";
         private static final String KEY_IDLE_PENDING_FACTOR = "idle_pending_factor";
+        private static final String KEY_QUICK_DOZE_DELAY_TIMEOUT = "quick_doze_delay_to";
         private static final String KEY_IDLE_TIMEOUT = "idle_to";
         private static final String KEY_MAX_IDLE_TIMEOUT = "max_idle_to";
         private static final String KEY_IDLE_FACTOR = "idle_factor";
@@ -866,6 +901,15 @@
         public float IDLE_PENDING_FACTOR;
 
         /**
+         * This is amount of time we will wait from the point where we go into
+         * STATE_QUICK_DOZE_DELAY until we actually go into STATE_IDLE, while waiting for jobs
+         * and other current activity to finish.
+         * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+         * @see #KEY_QUICK_DOZE_DELAY_TIMEOUT
+         */
+        public long QUICK_DOZE_DELAY_TIMEOUT;
+
+        /**
          * This is the initial time that we want to sit in the idle state before waking up
          * again to return to pending idle and allowing normal work to run.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
@@ -1001,6 +1045,8 @@
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
                 IDLE_PENDING_FACTOR = mParser.getFloat(KEY_IDLE_PENDING_FACTOR,
                         2f);
+                QUICK_DOZE_DELAY_TIMEOUT = mParser.getDurationMillis(
+                        KEY_QUICK_DOZE_DELAY_TIMEOUT, !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L);
                 IDLE_TIMEOUT = mParser.getDurationMillis(KEY_IDLE_TIMEOUT,
                         !COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L);
                 MAX_IDLE_TIMEOUT = mParser.getDurationMillis(KEY_MAX_IDLE_TIMEOUT,
@@ -1095,6 +1141,10 @@
             pw.print("    "); pw.print(KEY_IDLE_PENDING_FACTOR); pw.print("=");
             pw.println(IDLE_PENDING_FACTOR);
 
+            pw.print("    "); pw.print(KEY_QUICK_DOZE_DELAY_TIMEOUT); pw.print("=");
+            TimeUtils.formatDuration(QUICK_DOZE_DELAY_TIMEOUT, pw);
+            pw.println();
+
             pw.print("    "); pw.print(KEY_IDLE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(IDLE_TIMEOUT, pw);
             pw.println();
@@ -1508,6 +1558,8 @@
 
     static class Injector {
         private final Context mContext;
+        private ConnectivityService mConnectivityService;
+        private LocationManager mLocationManager;
 
         Injector(Context ctx) {
             mContext = ctx;
@@ -1527,7 +1579,11 @@
         }
 
         ConnectivityService getConnectivityService() {
-            return (ConnectivityService) ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+            if (mConnectivityService == null) {
+                mConnectivityService = (ConnectivityService) ServiceManager.getService(
+                        Context.CONNECTIVITY_SERVICE);
+            }
+            return mConnectivityService;
         }
 
         Constants getConstants(DeviceIdleController controller, Handler handler,
@@ -1536,7 +1592,10 @@
         }
 
         LocationManager getLocationManager() {
-            return mContext.getSystemService(LocationManager.class);
+            if (mLocationManager == null) {
+                mLocationManager = mContext.getSystemService(LocationManager.class);
+            }
+            return mLocationManager;
         }
 
         MyHandler getHandler(DeviceIdleController controller) {
@@ -1666,7 +1725,6 @@
                 mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                         "deviceidle_going_idle");
                 mGoingIdleWakeLock.setReferenceCounted(true);
-                mConnectivityService = mInjector.getConnectivityService();
                 mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                 mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
@@ -1689,7 +1747,6 @@
 
                 if (getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
-                    mLocationManager = mInjector.getLocationManager();
                     mLocationRequest = new LocationRequest()
                         .setQuality(LocationRequest.ACCURACY_FINE)
                         .setInterval(0)
@@ -1733,6 +1790,16 @@
                         mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray);
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
 
+                mLocalPowerManager.registerLowPowerModeObserver(ServiceType.QUICK_DOZE,
+                        state -> {
+                            synchronized (DeviceIdleController.this) {
+                                updateQuickDozeFlagLocked(state.batterySaverEnabled);
+                            }
+                        });
+                updateQuickDozeFlagLocked(
+                        mLocalPowerManager.getLowPowerState(
+                                ServiceType.QUICK_DOZE).batterySaverEnabled);
+
                 mLocalActivityTaskManager.registerScreenObserver(mScreenObserver);
 
                 passWhiteListsToForceAppStandbyTrackerLocked();
@@ -2160,10 +2227,17 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isNetworkConnected() {
+        synchronized (this) {
+            return mNetworkConnected;
+        }
+    }
+
     void updateConnectivityState(Intent connIntent) {
         ConnectivityService cm;
         synchronized (this) {
-            cm = mConnectivityService;
+            cm = mInjector.getConnectivityService();
         }
         if (cm == null) {
             return;
@@ -2199,7 +2273,9 @@
 
     @VisibleForTesting
     boolean isScreenOn() {
-        return mScreenOn;
+        synchronized (this) {
+            return mScreenOn;
+        }
     }
 
     void updateInteractivityLocked() {
@@ -2223,7 +2299,9 @@
 
     @VisibleForTesting
     boolean isCharging() {
-        return mCharging;
+        synchronized (this) {
+            return mCharging;
+        }
     }
 
     void updateChargingLocked(boolean charging) {
@@ -2241,6 +2319,27 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isQuickDozeEnabled() {
+        synchronized (this) {
+            return mQuickDozeActivated;
+        }
+    }
+
+    /** Updates the quick doze flag and enters deep doze if appropriate. */
+    @VisibleForTesting
+    void updateQuickDozeFlagLocked(boolean enabled) {
+        if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
+        mQuickDozeActivated = enabled;
+        if (enabled) {
+            // If Quick Doze is enabled, see if we should go straight into it.
+            becomeInactiveIfAppropriateLocked();
+        }
+        // Going from Deep Doze to Light Idle (if quick doze becomes disabled) is tricky and
+        // probably not worth the overhead, so leave in deep doze if that's the case until the
+        // next natural time to come out of it.
+    }
+
     void keyguardShowingLocked(boolean showing) {
         if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing);
         if (mScreenLocked != showing) {
@@ -2276,26 +2375,50 @@
     /** Must only be used in tests. */
     @VisibleForTesting
     void setDeepEnabledForTest(boolean enabled) {
-        mDeepEnabled = enabled;
+        synchronized (this) {
+            mDeepEnabled = enabled;
+        }
     }
 
     /** Must only be used in tests. */
     @VisibleForTesting
     void setLightEnabledForTest(boolean enabled) {
-        mLightEnabled = enabled;
+        synchronized (this) {
+            mLightEnabled = enabled;
+        }
     }
 
     void becomeInactiveIfAppropriateLocked() {
         if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
         if ((!mScreenOn && !mCharging) || mForceIdle) {
-            // Screen has turned off; we are now going to become inactive and start
-            // waiting to see if we will ultimately go idle.
-            if (mState == STATE_ACTIVE && mDeepEnabled) {
-                mState = STATE_INACTIVE;
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
-                resetIdleManagementLocked();
-                scheduleAlarmLocked(mInactiveTimeout, false);
-                EventLogTags.writeDeviceIdle(mState, "no activity");
+            // Become inactive and determine if we will ultimately go idle.
+            if (mDeepEnabled) {
+                if (mQuickDozeActivated) {
+                    if (mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
+                            || mState == STATE_IDLE_MAINTENANCE) {
+                        // Already "idling". Don't want to restart the process.
+                        // mLightState can't be LIGHT_STATE_ACTIVE if mState is any of these 3
+                        // values, so returning here is safe.
+                        return;
+                    }
+                    if (DEBUG) {
+                        Slog.d(TAG, "Moved from "
+                                + stateToString(mState) + " to STATE_QUICK_DOZE_DELAY");
+                    }
+                    mState = STATE_QUICK_DOZE_DELAY;
+                    // Make sure any motion sensing or locating is stopped.
+                    resetIdleManagementLocked();
+                    // Wait a small amount of time in case something (eg: background service from
+                    // recently closed app) needs to finish running.
+                    scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false);
+                    EventLogTags.writeDeviceIdle(mState, "no activity");
+                } else if (mState == STATE_ACTIVE) {
+                    mState = STATE_INACTIVE;
+                    if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
+                    resetIdleManagementLocked();
+                    scheduleAlarmLocked(mInactiveTimeout, false);
+                    EventLogTags.writeDeviceIdle(mState, "no activity");
+                }
             }
             if (mLightState == LIGHT_STATE_ACTIVE && mLightEnabled) {
                 mLightState = LIGHT_STATE_INACTIVE;
@@ -2338,7 +2461,9 @@
      */
     @VisibleForTesting
     void setLightStateForTest(int lightState) {
-        mLightState = lightState;
+        synchronized (this) {
+            mLightState = lightState;
+        }
     }
 
     @VisibleForTesting
@@ -2429,12 +2554,6 @@
         }
     }
 
-    /** Must only be used in tests. */
-    @VisibleForTesting
-    void setLocationManagerForTest(LocationManager lm) {
-        mLocationManager = lm;
-    }
-
     @VisibleForTesting
     int getState() {
         return mState;
@@ -2461,9 +2580,6 @@
                 // for motion and sleep some more while doing so.
                 startMonitoringMotionLocked();
                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
-                // Reset the upcoming idle delays.
-                mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
-                mNextIdleDelay = mConstants.IDLE_TIMEOUT;
                 mState = STATE_IDLE_PENDING;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING.");
                 EventLogTags.writeDeviceIdle(mState, reason);
@@ -2486,18 +2602,19 @@
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
                 EventLogTags.writeDeviceIdle(mState, reason);
                 scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false);
-                if (mLocationManager != null
-                        && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
-                    mLocationManager.requestLocationUpdates(mLocationRequest,
+                LocationManager locationManager = mInjector.getLocationManager();
+                if (locationManager != null
+                        && locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
+                    locationManager.requestLocationUpdates(mLocationRequest,
                             mGenericLocationListener, mHandler.getLooper());
                     mLocating = true;
                 } else {
                     mHasNetworkLocation = false;
                 }
-                if (mLocationManager != null
-                        && mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
+                if (locationManager != null
+                        && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
                     mHasGps = true;
-                    mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
+                    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
                             mGpsLocationListener, mHandler.getLooper());
                     mLocating = true;
                 } else {
@@ -2515,6 +2632,13 @@
                 cancelLocatingLocked();
                 mAnyMotionDetector.stop();
 
+                // Intentional fallthrough -- time to go into IDLE state.
+            case STATE_QUICK_DOZE_DELAY:
+                // Reset the upcoming idle delays.
+                mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
+                mNextIdleDelay = mConstants.IDLE_TIMEOUT;
+
+                // Everything is in place to go into IDLE state.
             case STATE_IDLE_MAINTENANCE:
                 scheduleAlarmLocked(mNextIdleDelay, true);
                 if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
@@ -2575,7 +2699,9 @@
     /** Must only be used in tests. */
     @VisibleForTesting
     void setActiveIdleOpsForTest(int count) {
-        mActiveIdleOpCount = count;
+        synchronized (this) {
+            mActiveIdleOpCount = count;
+        }
     }
 
     void setJobsActive(boolean active) {
@@ -2751,8 +2877,9 @@
 
     void cancelLocatingLocked() {
         if (mLocating) {
-            mLocationManager.removeUpdates(mGenericLocationListener);
-            mLocationManager.removeUpdates(mGpsLocationListener);
+            LocationManager locationManager = mInjector.getLocationManager();
+            locationManager.removeUpdates(mGenericLocationListener);
+            locationManager.removeUpdates(mGpsLocationListener);
             mLocating = false;
         }
     }
@@ -2766,11 +2893,15 @@
 
     void scheduleAlarmLocked(long delay, boolean idleUntil) {
         if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
-        if (mMotionSensor == null) {
+        if (mMotionSensor == null && !(mState == STATE_QUICK_DOZE_DELAY || mState == STATE_IDLE
+                  || mState == STATE_IDLE_MAINTENANCE)) {
             // If there is no motion sensor on this device, then we won't schedule
             // alarms, because we can't determine if the device is not moving.  This effectively
             // turns off normal execution of device idling, although it is still possible to
             // manually poke it by pretending like the alarm is going off.
+            // STATE_QUICK_DOZE_DELAY skips the motion sensing so if the state is past the motion
+            // sensing stage (ie, is QUICK_DOZE_DELAY, IDLE, or IDLE_MAINTENANCE), then idling
+            // can continue until the user interacts with the device.
             return;
         }
         mNextAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -3199,6 +3330,7 @@
                             case "light": pw.println(lightStateToString(mLightState)); break;
                             case "deep": pw.println(stateToString(mState)); break;
                             case "force": pw.println(mForceIdle); break;
+                            case "quick": pw.println(mQuickDozeActivated); break;
                             case "screen": pw.println(mScreenOn); break;
                             case "charging": pw.println(mCharging); break;
                             case "network": pw.println(mNetworkConnected); break;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 232c151..93bdcbb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -76,6 +76,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -99,6 +100,7 @@
 import com.android.server.location.LocationRequestStatistics.PackageStatistics;
 import com.android.server.location.MockProvider;
 import com.android.server.location.PassiveProvider;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -1764,9 +1766,7 @@
 
         if (enabled) {
             p.enable();
-            if (listeners > 0) {
-                applyRequirementsLocked(provider);
-            }
+            applyRequirementsLocked(provider);
         } else {
             p.disable();
         }
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index 6677541..2dee3a0 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -27,6 +27,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.format.DateFormat;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
@@ -91,7 +92,9 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        List<LooperStats.ExportedEntry> entries = mStats.getEntries();
+        pw.print("Start time: ");
+        pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis()));
+        final List<LooperStats.ExportedEntry> entries = mStats.getEntries();
         entries.sort(Comparator
                 .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
                 .thenComparing(entry -> entry.threadName)
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index cf39e95..f510d83 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -170,19 +170,6 @@
      */
     public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
 
-    /**
-     * String to pass to netd to indicate that a network is only accessible
-     * to apps that have the CHANGE_NETWORK_STATE permission.
-     */
-    public static final String PERMISSION_NETWORK = "NETWORK";
-
-    /**
-     * String to pass to netd to indicate that a network is only
-     * accessible to system apps and those with the CONNECTIVITY_INTERNAL
-     * permission.
-     */
-    public static final String PERMISSION_SYSTEM = "SYSTEM";
-
     static class NetdResponseCode {
         /* Keep in sync with system/netd/server/ResponseCode.h */
         public static final int InterfaceListResult       = 110;
@@ -223,6 +210,9 @@
 
     static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
 
+    static final boolean MODIFY_OPERATION_ADD = true;
+    static final boolean MODIFY_OPERATION_REMOVE = false;
+
     /**
      * Binder context for this service
      */
@@ -1122,41 +1112,47 @@
 
     @Override
     public void addRoute(int netId, RouteInfo route) {
-        modifyRoute("add", "" + netId, route);
+        modifyRoute(MODIFY_OPERATION_ADD, netId, route);
     }
 
     @Override
     public void removeRoute(int netId, RouteInfo route) {
-        modifyRoute("remove", "" + netId, route);
+        modifyRoute(MODIFY_OPERATION_REMOVE, netId, route);
     }
 
-    private void modifyRoute(String action, String netId, RouteInfo route) {
+    private void modifyRoute(boolean add, int netId, RouteInfo route) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final Command cmd = new Command("network", "route", action, netId);
-
-        // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
-        cmd.appendArg(route.getInterface());
-        cmd.appendArg(route.getDestination().toString());
+        final String ifName = route.getInterface();
+        final String dst = route.getDestination().toString();
+        final String nextHop;
 
         switch (route.getType()) {
             case RouteInfo.RTN_UNICAST:
                 if (route.hasGateway()) {
-                    cmd.appendArg(route.getGateway().getHostAddress());
+                    nextHop = route.getGateway().getHostAddress();
+                } else {
+                    nextHop = INetd.NEXTHOP_NONE;
                 }
                 break;
             case RouteInfo.RTN_UNREACHABLE:
-                cmd.appendArg("unreachable");
+                nextHop = INetd.NEXTHOP_UNREACHABLE;
                 break;
             case RouteInfo.RTN_THROW:
-                cmd.appendArg("throw");
+                nextHop = INetd.NEXTHOP_THROW;
+                break;
+            default:
+                nextHop = INetd.NEXTHOP_NONE;
                 break;
         }
-
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            if (add) {
+                mNetdService.networkAddRoute(netId, ifName, dst, nextHop);
+            } else {
+                mNetdService.networkRemoveRoute(netId, ifName, dst, nextHop);
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -1916,44 +1912,21 @@
     @Override
     public void addVpnUidRanges(int netId, UidRange[] ranges) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
-        argv[0] = "users";
-        argv[1] = "add";
-        argv[2] = netId;
-        int argc = 3;
-        // Avoid overly long commands by limiting number of UID ranges per command.
-        for (int i = 0; i < ranges.length; i++) {
-            argv[argc++] = ranges[i].toString();
-            if (i == (ranges.length - 1) || argc == argv.length) {
-                try {
-                    mConnector.execute("network", Arrays.copyOf(argv, argc));
-                } catch (NativeDaemonConnectorException e) {
-                    throw e.rethrowAsParcelableException();
-                }
-                argc = 3;
-            }
+
+        try {
+            mNetdService.networkAddUidRanges(netId, ranges);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
     @Override
     public void removeVpnUidRanges(int netId, UidRange[] ranges) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
-        argv[0] = "users";
-        argv[1] = "remove";
-        argv[2] = netId;
-        int argc = 3;
-        // Avoid overly long commands by limiting number of UID ranges per command.
-        for (int i = 0; i < ranges.length; i++) {
-            argv[argc++] = ranges[i].toString();
-            if (i == (ranges.length - 1) || argc == argv.length) {
-                try {
-                    mConnector.execute("network", Arrays.copyOf(argv, argc));
-                } catch (NativeDaemonConnectorException e) {
-                    throw e.rethrowAsParcelableException();
-                }
-                argc = 3;
-            }
+        try {
+            mNetdService.networkRemoveUidRanges(netId, ranges);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2412,17 +2385,13 @@
     }
 
     @Override
-    public void createPhysicalNetwork(int netId, String permission) {
+    public void createPhysicalNetwork(int netId, int permission) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            if (permission != null) {
-                mConnector.execute("network", "create", netId, permission);
-            } else {
-                mConnector.execute("network", "create", netId);
-            }
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkCreatePhysical(netId, permission);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2431,10 +2400,9 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mConnector.execute("network", "create", netId, "vpn", hasDNS ? "1" : "0",
-                    secure ? "1" : "0");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkCreateVpn(netId, hasDNS, secure);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2455,20 +2423,24 @@
 
     @Override
     public void addInterfaceToNetwork(String iface, int netId) {
-        modifyInterfaceInNetwork("add", "" + netId, iface);
+        modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, netId, iface);
     }
 
     @Override
     public void removeInterfaceFromNetwork(String iface, int netId) {
-        modifyInterfaceInNetwork("remove", "" + netId, iface);
+        modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, netId, iface);
     }
 
-    private void modifyInterfaceInNetwork(String action, String netId, String iface) {
+    private void modifyInterfaceInNetwork(boolean add, int netId, String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("network", "interface", action, netId, iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            if (add) {
+                mNetdService.networkAddInterface(netId, iface);
+            } else {
+                mNetdService.networkRemoveInterface(netId, iface);
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2476,20 +2448,20 @@
     public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        final Command cmd = new Command("network", "route", "legacy", uid, "add", netId);
-
-        // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
         final LinkAddress la = routeInfo.getDestinationLinkAddress();
-        cmd.appendArg(routeInfo.getInterface());
-        cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getPrefixLength());
-        if (routeInfo.hasGateway()) {
-            cmd.appendArg(routeInfo.getGateway().getHostAddress());
-        }
+        final String ifName = routeInfo.getInterface();
+        final String dst = la.toString();
+        final String nextHop;
 
+        if (routeInfo.hasGateway()) {
+            nextHop = routeInfo.getGateway().getHostAddress();
+        } else {
+            nextHop = "";
+        }
         try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkAddLegacyRoute(netId, ifName, dst, nextHop, uid);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2498,9 +2470,9 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mConnector.execute("network", "default", "set", netId);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkSetDefault(netId);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2509,49 +2481,41 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mConnector.execute("network", "default", "clear");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkClearDefault();
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
     @Override
-    public void setNetworkPermission(int netId, String permission) {
+    public void setNetworkPermission(int netId, int permission) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            if (permission != null) {
-                mConnector.execute("network", "permission", "network", "set", permission, netId);
-            } else {
-                mConnector.execute("network", "permission", "network", "clear", netId);
-            }
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkSetPermissionForNetwork(netId, permission);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
+    private int parsePermission(String permission) {
+        if (permission.equals("NETWORK")) {
+            return INetd.PERMISSION_NETWORK;
+        }
+        if (permission.equals("SYSTEM")) {
+            return INetd.PERMISSION_SYSTEM;
+        }
+        return INetd.PERMISSION_NONE;
+    }
 
     @Override
     public void setPermission(String permission, int[] uids) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        Object[] argv = new Object[4 + MAX_UID_RANGES_PER_COMMAND];
-        argv[0] = "permission";
-        argv[1] = "user";
-        argv[2] = "set";
-        argv[3] = permission;
-        int argc = 4;
-        // Avoid overly long commands by limiting number of UIDs per command.
-        for (int i = 0; i < uids.length; ++i) {
-            argv[argc++] = uids[i];
-            if (i == uids.length - 1 || argc == argv.length) {
-                try {
-                    mConnector.execute("network", Arrays.copyOf(argv, argc));
-                } catch (NativeDaemonConnectorException e) {
-                    throw e.rethrowAsParcelableException();
-                }
-                argc = 4;
-            }
+        try {
+            mNetdService.networkSetPermissionForUser(parsePermission(permission), uids);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2559,22 +2523,10 @@
     public void clearPermission(int[] uids) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
-        Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
-        argv[0] = "permission";
-        argv[1] = "user";
-        argv[2] = "clear";
-        int argc = 3;
-        // Avoid overly long commands by limiting number of UIDs per command.
-        for (int i = 0; i < uids.length; ++i) {
-            argv[argc++] = uids[i];
-            if (i == uids.length - 1 || argc == argv.length) {
-                try {
-                    mConnector.execute("network", Arrays.copyOf(argv, argc));
-                } catch (NativeDaemonConnectorException e) {
-                    throw e.rethrowAsParcelableException();
-                }
-                argc = 3;
-            }
+        try {
+            mNetdService.networkClearPermissionForUser(uids);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2583,9 +2535,9 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mConnector.execute("network", "protect", "allow", uid);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkSetProtectAllow(uid);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
@@ -2594,26 +2546,26 @@
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         try {
-            mConnector.execute("network", "protect", "deny", uid);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            mNetdService.networkSetProtectDeny(uid);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw new IllegalStateException(e);
         }
     }
 
     @Override
     public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
-        modifyInterfaceInNetwork("add", "local", iface);
+        modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.NETID_LOCAL, iface);
 
         for (RouteInfo route : routes) {
             if (!route.isDefaultRoute()) {
-                modifyRoute("add", "local", route);
+                modifyRoute(MODIFY_OPERATION_ADD, INetd.NETID_LOCAL, route);
             }
         }
     }
 
     @Override
     public void removeInterfaceFromLocalNetwork(String iface) {
-        modifyInterfaceInNetwork("remove", "local", iface);
+        modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, INetd.NETID_LOCAL, iface);
     }
 
     @Override
@@ -2622,7 +2574,7 @@
 
         for (RouteInfo route : routes) {
             try {
-                modifyRoute("remove", "local", route);
+                modifyRoute(MODIFY_OPERATION_REMOVE, INetd.NETID_LOCAL, route);
             } catch (IllegalStateException e) {
                 failures++;
             }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7c67596..858dced 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3150,10 +3150,10 @@
 
             if (toSystem) {
                 // Everything else goes into sandbox.
-                return device + "Android/sandbox/" + sandboxId.replace(':', '/') + "/" + devicePath;
+                return device + "Android/sandbox/" + sandboxId + "/" + devicePath;
             } else {
                 // Does path belong to this sandbox? If so, leave sandbox.
-                final String sandboxPrefix = "Android/sandbox/" + sandboxId.replace(':', '/') + "/";
+                final String sandboxPrefix = "Android/sandbox/" + sandboxId + "/";
                 if (devicePath.startsWith(sandboxPrefix)) {
                     return device + devicePath.substring(sandboxPrefix.length());
                 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 591ec00..65f3c03 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -216,6 +216,9 @@
 
     private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+    @TelephonyManager.RadioPowerState
+    private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE;
+
     private final LocalLog mLocalLog = new LocalLog(100);
 
     private PreciseDataConnectionState mPreciseDataConnectionState =
@@ -762,6 +765,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
+                        try {
+                            r.callback.onRadioPowerStateChanged(mRadioPowerState);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -1609,6 +1619,32 @@
         }
     }
 
+    public void notifyRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+        if (!checkNotifyPermission("notifyRadioPowerStateChanged()")) {
+            return;
+        }
+
+        if (VDBG) {
+            log("notifyRadioPowerStateChanged: state= " + state);
+        }
+
+        synchronized (mRecords) {
+            mRadioPowerState = state;
+
+            for (Record r : mRecords) {
+                if (r.matchPhoneStateListenerEvent(
+                        PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED)) {
+                    try {
+                        r.callback.onRadioPowerStateChanged(state);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -1646,6 +1682,7 @@
             pw.println("mVoLteServiceState=" + mVoLteServiceState);
             pw.println("mPhoneCapability=" + mPhoneCapability);
             pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
+            pw.println("mRadioPowerState=" + mRadioPowerState);
 
             pw.decreaseIndent();
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index b2be5e6..793a177 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -1270,26 +1270,36 @@
         public int onCommand(String cmd) {
             if ("vibrate".equals(cmd)) {
                 return runVibrate();
+            } else if ("prebaked".equals(cmd)) {
+                return runPrebaked();
             }
             return handleDefaultCommands(cmd);
         }
 
+        private boolean checkDoNotDisturb() {
+            try {
+                final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.ZEN_MODE);
+                if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                    try (PrintWriter pw = getOutPrintWriter();) {
+                        pw.print("Ignoring because device is on DND mode ");
+                        pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+                                zenMode));
+                        return true;
+                    }
+                }
+            } catch (SettingNotFoundException e) {
+                // ignore
+            }
+
+            return false;
+        }
+
         private int runVibrate() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
             try {
-                try {
-                    final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
-                            Settings.Global.ZEN_MODE);
-                    if (zenMode != Settings.Global.ZEN_MODE_OFF) {
-                        try (PrintWriter pw = getOutPrintWriter();) {
-                            pw.print("Ignoring because device is on DND mode ");
-                            pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
-                                    zenMode));
-                            return 0;
-                        }
-                    }
-                } catch (SettingNotFoundException e) {
-                    // ignore
+                if (checkDoNotDisturb()) {
+                    return 0;
                 }
 
                 final long duration = Long.parseLong(getNextArgRequired());
@@ -1311,6 +1321,30 @@
             }
         }
 
+        private int runPrebaked() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
+            try {
+                if (checkDoNotDisturb()) {
+                    return 0;
+                }
+
+                final int id = Integer.parseInt(getNextArgRequired());
+
+                String description = getNextArg();
+                if (description == null) {
+                    description = "Shell command";
+                }
+
+                VibrationEffect effect =
+                        VibrationEffect.get(id, false);
+                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
+                        "Shell Command", mToken);
+                return 0;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
         @Override
         public void onHelp() {
             try (PrintWriter pw = getOutPrintWriter();) {
@@ -1321,6 +1355,9 @@
                 pw.println("  vibrate duration [description]");
                 pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
                 pw.println("    (Do Not Disturb) mode.");
+                pw.println("  prebaked effect-id [description]");
+                pw.println("    Vibrates with prebaked effect; ignored when device is on DND ");
+                pw.println("    (Do Not Disturb) mode.");
                 pw.println("");
             }
         }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 0b836f0..9cc550d 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -528,7 +528,7 @@
             Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
                     public void run() {
                         mActivity.addErrorToDropBox(
-                                "watchdog", null, "system_server", null, null,
+                                "watchdog", null, "system_server", null, null, null,
                                 subject, null, stack, null);
                     }
                 };
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index fcda83d..3939bee 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -339,7 +339,8 @@
                         Slog.w(TAG, uei.getSwitchStatePath() +
                                 " not found while attempting to determine initial switch state");
                     } catch (Exception e) {
-                        Slog.e(TAG, "" , e);
+                        Slog.e(TAG, "Error while attempting to determine initial switch state for "
+                                + uei.getDevName() , e);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8e64b50..405a2d0 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -25,7 +25,6 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -552,7 +551,7 @@
         if (!callerFg && !fgRequired && r.app == null
                 && mAm.mUserController.hasStartedUserState(r.userId)) {
             ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
-            if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
+            if (proc == null || proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
                 // If this is not coming from a foreground caller, then we may want
                 // to delay the start if there are already other background services
                 // that are starting.  This is to avoid process start spam when lots
@@ -580,7 +579,7 @@
                 }
                 if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
                 addToStarting = true;
-            } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
+            } else if (proc.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
                 // We slightly loosen when we will enqueue this new service as a background
                 // starting service we are waiting for, to also include processes that are
                 // currently running other services or receivers.
@@ -589,7 +588,7 @@
                         "Not delaying, but counting as bg: " + r);
             } else if (DEBUG_DELAYED_STARTS) {
                 StringBuilder sb = new StringBuilder(128);
-                sb.append("Not potential delay (state=").append(proc.curProcState)
+                sb.append("Not potential delay (state=").append(proc.getCurProcState())
                         .append(' ').append(proc.adjType);
                 String reason = proc.makeAdjReason();
                 if (reason != null) {
@@ -1139,7 +1138,7 @@
             for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
                 ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
                 if (active.mUid == uidRec.uid) {
-                    if (uidRec.curProcState <= ActivityManager.PROCESS_STATE_TOP) {
+                    if (uidRec.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
                         if (!active.mAppOnTop) {
                             active.mAppOnTop = true;
                             changed = true;
@@ -1258,7 +1257,7 @@
                                 active.mShownWhileScreenOn = mScreenOn;
                                 if (r.app != null) {
                                     active.mAppOnTop = active.mShownWhileTop =
-                                            r.app.uidRecord.curProcState
+                                            r.app.uidRecord.getCurProcState()
                                                     <= ActivityManager.PROCESS_STATE_TOP;
                                 }
                                 active.mStartTime = active.mStartVisibleTime
@@ -1442,8 +1441,8 @@
                 }
             }
         }
-        if (anyClientActivities != proc.hasClientActivities) {
-            proc.hasClientActivities = anyClientActivities;
+        if (anyClientActivities != proc.hasClientActivities()) {
+            proc.setHasClientActivities(anyClientActivities);
             if (updateLru) {
                 mAm.updateLruProcessLocked(proc, anyClientActivities, null);
             }
@@ -1623,8 +1622,9 @@
                 }
             }
 
-            mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
-                    s.appInfo.uid, s.appInfo.longVersionCode, s.name, s.processName);
+            mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
+                    callerApp.getCurProcState(), s.appInfo.uid, s.appInfo.longVersionCode,
+                    s.name, s.processName);
             // Once the apps have become associated, if one of them is caller is ephemeral
             // the target app should now be able to see the calling app
             mAm.grantEphemeralAccessLocked(callerApp.userId, service,
@@ -1680,7 +1680,7 @@
                     s.app.whitelistManager = true;
                 }
                 // This could have made the service more important.
-                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
+                mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
                         || s.app.treatLikeActivity, b.client);
                 mAm.updateOomAdjLocked(s.app, true);
             }
@@ -1794,7 +1794,7 @@
                     if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                         r.binding.service.app.treatLikeActivity = true;
                         mAm.updateLruProcessLocked(r.binding.service.app,
-                                r.binding.service.app.hasClientActivities
+                                r.binding.service.app.hasClientActivities()
                                 || r.binding.service.app.treatLikeActivity, null);
                     }
                     mAm.updateOomAdjLocked(r.binding.service.app, false);
@@ -3259,9 +3259,9 @@
         }
     }
 
-    void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) {
+    void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
         ArrayList<ServiceRecord> services = new ArrayList<>();
-        ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(tr.userId);
+        ArrayMap<ComponentName, ServiceRecord> alls = getServicesLocked(userId);
         for (int i = alls.size() - 1; i >= 0; i--) {
             ServiceRecord sr = alls.valueAt(i);
             if (sr.packageName.equals(component.getPackageName())) {
@@ -3620,7 +3620,7 @@
                     nextTime = sr.executingStart;
                 }
             }
-            if (timeout != null && mAm.mLruProcesses.contains(proc)) {
+            if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
                 Slog.w(TAG, "Timeout executing service: " + timeout);
                 StringWriter sw = new StringWriter();
                 PrintWriter pw = new FastPrintWriter(sw, false, 1024);
@@ -3641,7 +3641,7 @@
         }
 
         if (anrMessage != null) {
-            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
+            proc.appNotResponding(null, null, null, null, false, anrMessage);
         }
     }
 
@@ -3666,7 +3666,7 @@
         }
 
         if (app != null) {
-            mAm.mAppErrors.appNotResponding(app, null, null, false,
+            app.appNotResponding(null, null, null, null, false,
                     "Context.startForegroundService() did not then call Service.startForeground(): "
                         + r);
         }
diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java
new file mode 100644
index 0000000..4e1435e
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActiveUids.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import android.util.SparseArray;
+
+/** Class for tracking active uids for running processes. */
+final class ActiveUids {
+
+    private ActivityManagerService mService;
+
+    private boolean mPostChangesToAtm;
+    private final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
+
+    ActiveUids(ActivityManagerService service, boolean postChangesToAtm) {
+        mService = service;
+        mPostChangesToAtm = postChangesToAtm;
+    }
+
+    void put(int uid, UidRecord value) {
+        mActiveUids.put(uid, value);
+        if (mPostChangesToAtm) {
+            mService.mAtmInternal.onUidActive(uid, value.getCurProcState());
+        }
+    }
+
+    void remove(int uid) {
+        mActiveUids.remove(uid);
+        if (mPostChangesToAtm) {
+            mService.mAtmInternal.onUidInactive(uid);
+        }
+    }
+
+    void clear() {
+        mActiveUids.clear();
+        if (mPostChangesToAtm) {
+            mService.mAtmInternal.onActiveUidsCleared();
+        }
+    }
+
+    UidRecord get(int uid) {
+        return mActiveUids.get(uid);
+    }
+
+    int size() {
+        return mActiveUids.size();
+    }
+
+    UidRecord valueAt(int index) {
+        return mActiveUids.valueAt(index);
+    }
+
+    int keyAt(int index) {
+        return mActiveUids.keyAt(index);
+    }
+
+    int indexOfKey(int uid) {
+        return mActiveUids.indexOfKey(uid);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 95a8e2a..ede13ef 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -37,16 +37,16 @@
 import static com.android.server.am.ActivityDisplayProto.ID;
 import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
 import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -73,7 +73,7 @@
  */
 class ActivityDisplay extends ConfigurationContainer<ActivityStack>
         implements WindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
 
     static final int POSITION_TOP = Integer.MAX_VALUE;
@@ -873,6 +873,52 @@
         return null;
     }
 
+    ActivityRecord topRunningActivity() {
+        return topRunningActivity(false /* considerKeyguardState */);
+    }
+
+    /**
+     * Returns the top running activity in the focused stack. In the case the focused stack has no
+     * such activity, the next focusable stack on this display is returned.
+     *
+     * @param considerKeyguardState Indicates whether the locked state should be considered. if
+     *                              {@code true} and the keyguard is locked, only activities that
+     *                              can be shown on top of the keyguard will be considered.
+     * @return The top running activity. {@code null} if none is available.
+     */
+    ActivityRecord topRunningActivity(boolean considerKeyguardState) {
+        ActivityRecord topRunning = null;
+        final ActivityStack focusedStack = getFocusedStack();
+        if (focusedStack != null) {
+            topRunning = focusedStack.topRunningActivityLocked();
+        }
+
+        // Look in other focusable stacks.
+        if (topRunning == null) {
+            for (int i = mStacks.size() - 1; i >= 0; --i) {
+                final ActivityStack stack = mStacks.get(i);
+                // Only consider focusable stacks other than the current focused one.
+                if (stack == focusedStack || !stack.isFocusable()) {
+                    continue;
+                }
+                topRunning = stack.topRunningActivityLocked();
+                if (topRunning != null) {
+                    break;
+                }
+            }
+        }
+
+        // This activity can be considered the top running activity if we are not considering
+        // the locked state, the keyguard isn't locked, or we can show when locked.
+        if (topRunning != null && considerKeyguardState
+                && mSupervisor.getKeyguardController().isKeyguardLocked()
+                && !topRunning.canShowWhenLocked()) {
+            return null;
+        }
+
+        return topRunning;
+    }
+
     int getIndexOf(ActivityStack stack) {
         return mStacks.indexOf(stack);
     }
@@ -998,7 +1044,10 @@
      * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
      */
     boolean supportsSystemDecorations() {
-        return mDisplay.supportsSystemDecorations();
+        return mDisplay.supportsSystemDecorations()
+                // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
+                // (b/114338689) whenever vr 2d display id is set.
+                || mDisplayId == mSupervisor.mService.mVr2dDisplayId;
     }
 
     private boolean shouldDestroyContentOnRemove() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 3a0289c..5c77f0a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -220,7 +220,7 @@
 
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
-    boolean mFlagActivityStartsLoggingEnabled;
+    volatile boolean mFlagActivityStartsLoggingEnabled;
 
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 0e63d0c..0aaea2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -41,97 +41,48 @@
     // Enable all debug log categories.
     static final boolean DEBUG_ALL = false;
 
-    // Enable all debug log categories for activities.
-    static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
-
     // Available log categories in the activity manager package.
-    static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_ANR = true;  // STOPSHIP disable it (b/113252928)
-    static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_BACKGROUND_CHECK = DEBUG_ALL || false;
     static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
     static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
     static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false;
     static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
-    static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;
-    static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
-    static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_FOCUS = false;
-    static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
-    static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
     static final boolean DEBUG_LRU = DEBUG_ALL || false;
     static final boolean DEBUG_MU = DEBUG_ALL || false;
     static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ_REASON = DEBUG_ALL || false;
-    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
     static final boolean DEBUG_POWER = DEBUG_ALL || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
     static final boolean DEBUG_PROCESSES = DEBUG_ALL || false;
     static final boolean DEBUG_PROVIDER = DEBUG_ALL || false;
     static final boolean DEBUG_PSS = DEBUG_ALL || false;
-    static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
-    static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
-    static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
-    static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
-    static final boolean DEBUG_STACK = DEBUG_ALL || false;
-    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
-    static final boolean DEBUG_TASKS = DEBUG_ALL || false;
-    static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
     static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false;
-    static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
-    static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
-    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
     static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
     static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
     static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false;
-    static final boolean DEBUG_METRICS = DEBUG_ALL || false;
 
-    static final String POSTFIX_ADD_REMOVE = (APPEND_CATEGORY_NAME) ? "_AddRemove" : "";
-    static final String POSTFIX_APP = (APPEND_CATEGORY_NAME) ? "_App" : "";
     static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : "";
     static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : "";
     static final String POSTFIX_CLEANUP = (APPEND_CATEGORY_NAME) ? "_Cleanup" : "";
-    static final String POSTFIX_CONFIGURATION = (APPEND_CATEGORY_NAME) ? "_Configuration" : "";
-    static final String POSTFIX_CONTAINERS = (APPEND_CATEGORY_NAME) ? "_Containers" : "";
-    static final String POSTFIX_FOCUS = (APPEND_CATEGORY_NAME) ? "_Focus" : "";
-    static final String POSTFIX_IDLE = (APPEND_CATEGORY_NAME) ? "_Idle" : "";
-    static final String POSTFIX_IMMERSIVE = (APPEND_CATEGORY_NAME) ? "_Immersive" : "";
-    static final String POSTFIX_LOCKTASK = (APPEND_CATEGORY_NAME) ? "_LockTask" : "";
     static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : "";
     static final String POSTFIX_MU = "_MU";
     static final String POSTFIX_NETWORK = "_Network";
     static final String POSTFIX_OOM_ADJ = (APPEND_CATEGORY_NAME) ? "_OomAdj" : "";
-    static final String POSTFIX_PAUSE = (APPEND_CATEGORY_NAME) ? "_Pause" : "";
     static final String POSTFIX_POWER = (APPEND_CATEGORY_NAME) ? "_Power" : "";
     static final String POSTFIX_PROCESS_OBSERVERS = (APPEND_CATEGORY_NAME)
             ? "_ProcessObservers" : "";
     static final String POSTFIX_PROCESSES = (APPEND_CATEGORY_NAME) ? "_Processes" : "";
     static final String POSTFIX_PROVIDER = (APPEND_CATEGORY_NAME) ? "_Provider" : "";
     static final String POSTFIX_PSS = (APPEND_CATEGORY_NAME) ? "_Pss" : "";
-    static final String POSTFIX_RECENTS = (APPEND_CATEGORY_NAME) ? "_Recents" : "";
-    static final String POSTFIX_RELEASE = (APPEND_CATEGORY_NAME) ? "_Release" : "";
-    static final String POSTFIX_RESULTS = (APPEND_CATEGORY_NAME) ? "_Results" : "";
-    static final String POSTFIX_SAVED_STATE = (APPEND_CATEGORY_NAME) ? "_SavedState" : "";
     static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
     static final String POSTFIX_SERVICE_EXECUTING =
             (APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : "";
-    static final String POSTFIX_STACK = (APPEND_CATEGORY_NAME) ? "_Stack" : "";
-    static final String POSTFIX_STATES = (APPEND_CATEGORY_NAME) ? "_States" : "";
-    static final String POSTFIX_SWITCH = (APPEND_CATEGORY_NAME) ? "_Switch" : "";
-    static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
-    static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
     static final String POSTFIX_UID_OBSERVERS = (APPEND_CATEGORY_NAME)
             ? "_UidObservers" : "";
-    static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
-    static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
-    static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
-
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index acf7a73..1b74ed0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,19 +18,19 @@
 
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+import static android.Manifest.permission.FILTER_EVENTS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.REMOVE_TASKS;
+import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
+import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
 import static android.app.AppOpsManager.OP_NONE;
-import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_PROVIDERS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -49,8 +49,6 @@
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
 import static android.os.Process.BLUETOOTH_UID;
 import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.Process.FIRST_ISOLATED_UID;
-import static android.os.Process.LAST_ISOLATED_UID;
 import static android.os.Process.NFC_UID;
 import static android.os.Process.PHONE_UID;
 import static android.os.Process.PROC_CHAR;
@@ -69,10 +67,10 @@
 import static android.os.Process.THREAD_GROUP_DEFAULT;
 import static android.os.Process.THREAD_GROUP_RESTRICTED;
 import static android.os.Process.THREAD_GROUP_TOP_APP;
-import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-import static android.os.Process.getFreeMemory;
+import static android.os.Process.getPidsForCommands;
 import static android.os.Process.getTotalMemory;
+import static android.os.Process.getUidForPid;
 import static android.os.Process.isThreadInProcess;
 import static android.os.Process.killProcess;
 import static android.os.Process.killProcessQuiet;
@@ -84,14 +82,12 @@
 import static android.os.Process.setProcessGroup;
 import static android.os.Process.setThreadPriority;
 import static android.os.Process.setThreadScheduler;
-import static android.os.Process.startWebView;
 import static android.os.Process.zygoteProcess;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
@@ -100,9 +96,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
@@ -115,15 +108,12 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_NETWORK;
@@ -134,13 +124,32 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_ACTIVITIES_SHORT_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_CONTAINERS_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_LASTANR_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_LASTANR_TRACES_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_RECENTS_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD;
+import static com.android.server.am.ActivityTaskManagerService.DUMP_STARTER_CMD;
+import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.am.ActivityTaskManagerService.relaunchReasonToString;
+import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES;
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
+import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
 
 import android.Manifest;
 import android.Manifest.permission;
@@ -153,7 +162,6 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerProto;
-import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -182,6 +190,7 @@
 import android.app.PendingIntent;
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
+import android.app.WaitResult;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
 import android.app.backup.IBackupManager;
@@ -202,7 +211,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy;
-import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
@@ -252,7 +260,6 @@
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManagerInternal;
 import android.os.Process;
-import android.os.Process.ProcessStartResult;
 import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -267,9 +274,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
-import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
-import android.os.storage.StorageManagerInternal;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -279,7 +284,6 @@
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.LongSparseArray;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -315,7 +319,6 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.os.Zygote;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -341,13 +344,11 @@
 import com.android.server.ThreadPriorityBooster;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
-import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.MemoryStatUtil.MemoryStat;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
-import com.android.server.pm.dex.DexManager;
 import com.android.server.uri.GrantUri;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.PriorityDump;
@@ -359,18 +360,15 @@
 
 import libcore.util.EmptyArray;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
-import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -399,24 +397,24 @@
      */
     public static final int TOP_APP_PRIORITY_BOOST = -10;
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
+    static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
     private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
     private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
-    private static final String TAG_LRU = TAG + POSTFIX_LRU;
+    static final String TAG_LRU = TAG + POSTFIX_LRU;
     private static final String TAG_MU = TAG + POSTFIX_MU;
     private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK;
     private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
     private static final String TAG_POWER = TAG + POSTFIX_POWER;
     private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
-    private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
+    static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
     private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
-    private static final String TAG_PSS = TAG + POSTFIX_PSS;
+    static final String TAG_PSS = TAG + POSTFIX_PSS;
     private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
+    static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
 
     // Mock "pretend we're idle now" broadcast action to the job scheduler; declared
     // here so that while the job scheduler can depend on AMS, the other way around
@@ -434,11 +432,11 @@
     static final boolean MONITOR_THREAD_CPU_USAGE = false;
 
     // The flags that are set for all calls we make to the package manager.
-    static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
+    public static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
 
     static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
-    private static final String ANR_TRACE_DIR = "/data/anr";
+    public static final String ANR_TRACE_DIR = "/data/anr";
 
     // Maximum number of receivers an app can register.
     private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
@@ -450,6 +448,12 @@
     // before we decide it must be hung.
     static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
 
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
+     */
+    static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
+
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
     // started with a wrapper for instrumentation (such as Valgrind) because it
@@ -460,11 +464,7 @@
     static final int BROADCAST_FG_TIMEOUT = 10*1000;
     static final int BROADCAST_BG_TIMEOUT = 60*1000;
 
-    // Disable hidden API checks for the newly started instrumentation.
-    // Must be kept in sync with Am.
-    private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
-
-    static final int MY_PID = myPid();
+    public static final int MY_PID = myPid();
 
     static final String[] EMPTY_STRING_ARRAY = new String[0];
 
@@ -478,16 +478,13 @@
     private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20;
 
     // Necessary ApplicationInfo flags to mark an app as persistent
-    private static final int PERSISTENT_MASK =
+    static final int PERSISTENT_MASK =
             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
 
     // Intent sent when remote bugreport collection has been completed
     private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
             "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
 
-    // Used to indicate that an app transition should be animated.
-    static final boolean ANIMATE = true;
-
     // If set, we will push process association information in to procstats.
     static final boolean TRACK_PROCSTATS_ASSOCIATIONS = true;
 
@@ -496,6 +493,9 @@
      */
     private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 200; // 0.2 sec
 
+    // The minimum memory growth threshold (in KB) for low RAM devices.
+    private static final int MINIMUM_MEMORY_GROWTH_THRESHOLD = 10 * 1000; // 10 MB
+
     /**
      * State indicating that there is no need for any blocking for network.
      */
@@ -525,9 +525,6 @@
 
     private Installer mInstaller;
 
-    /** Run all ActivityStacks through this */
-    ActivityStackSupervisor mStackSupervisor;
-
     final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
 
     final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>();
@@ -567,12 +564,6 @@
     final AppErrors mAppErrors;
 
     /**
-     * Dump of the activity state at the time of the last ANR. Cleared after
-     * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
-     */
-    String mLastANRState;
-
-    /**
      * Indicates the maximum time spent waiting for the network rules to get updated.
      */
     @VisibleForTesting
@@ -625,46 +616,12 @@
     final ProcessList mProcessList = new ProcessList();
 
     /**
-     * All of the applications we currently have running organized by name.
-     * The keys are strings of the application package name (as
-     * returned by the package manager), and the keys are ApplicationRecord
-     * objects.
-     */
-    final MyProcessMap mProcessNames = new MyProcessMap();
-    final class MyProcessMap extends ProcessMap<ProcessRecord> {
-        @Override
-        public ProcessRecord put(String name, int uid, ProcessRecord value) {
-            final ProcessRecord r = super.put(name, uid, value);
-            mAtmInternal.onProcessAdded(r.getWindowProcessController());
-            return r;
-        }
-
-        @Override
-        public ProcessRecord remove(String name, int uid) {
-            final ProcessRecord r = super.remove(name, uid);
-            mAtmInternal.onProcessRemoved(name, uid);
-            return r;
-        }
-    }
-
-    /**
      * Tracking long-term execution of processes to look for abuse and other
      * bad app behavior.
      */
     final ProcessStatsService mProcessStats;
 
     /**
-     * The currently running isolated processes.
-     */
-    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
-
-    /**
-     * Counter for assigning isolated process uids, to avoid frequently reusing the
-     * same ones.
-     */
-    int mNextIsolatedProcessUid = 0;
-
-    /**
      * Non-persistent appId whitelist for background restrictions
      */
     int[] mBackgroundAppIdWhitelist = new int[] {
@@ -765,28 +722,6 @@
     final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
 
     /**
-     * Processes that are being forcibly torn down.
-     */
-    final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
-
-    /**
-     * List of running applications, sorted by recent usage.
-     * The first entry in the list is the least recently used.
-     */
-    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
-
-    /**
-     * Where in mLruProcesses that the processes hosting activities start.
-     */
-    int mLruProcessActivityStart = 0;
-
-    /**
-     * Where in mLruProcesses that the processes hosting services start.
-     * This is after (lower index) than mLruProcessesActivityStart.
-     */
-    int mLruProcessServiceStart = 0;
-
-    /**
      * List of processes that should gc as soon as things are idle.
      */
     final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
@@ -809,16 +744,14 @@
      */
     boolean mFullPssPending = false;
 
-    /**
-     * Track all uids that have actively running processes.
-     */
-    final SparseArray<UidRecord> mActiveUids = new SparseArray<>();
+    /** Track all uids that have actively running processes. */
+    final ActiveUids mActiveUids;
 
     /**
      * This is for verifying the UID report flow.
      */
     static final boolean VALIDATE_UID_STATES = true;
-    final SparseArray<UidRecord> mValidateUids = new SparseArray<>();
+    final ActiveUids mValidateUids = new ActiveUids(this, false /* postChangesToAtm */);
 
     /**
      * Fingerprints (hashCode()) of stack traces that we've
@@ -1045,7 +978,7 @@
         }
     }
 
-    final SparseArray<PendingTempWhitelist> mPendingTempWhitelist = new SparseArray<>();
+    final PendingTempWhitelists mPendingTempWhitelist = new PendingTempWhitelists(this);
 
     /**
      * Information about and control over application operations
@@ -1053,11 +986,6 @@
     final AppOpsService mAppOpsService;
 
     /**
-     * Hardware-reported OpenGLES version.
-     */
-    final int GL_ES_VERSION;
-
-    /**
      * List of initialization arguments to pass to all processes when binding applications to them.
      * For example, references to the commonly used services.
      */
@@ -1077,7 +1005,6 @@
 
     @GuardedBy("this") boolean mCallFinishBooting = false;
     @GuardedBy("this") boolean mBootAnimationComplete = false;
-    private @GuardedBy("this") boolean mCheckedForSetup = false;
 
     final Context mContext;
 
@@ -1108,11 +1035,6 @@
     int mAdjSeq = 0;
 
     /**
-     * Current sequence id for process LRU updating.
-     */
-    int mLruSeq = 0;
-
-    /**
      * Keep track of the non-cached/empty process we last found, to help
      * determine how to distribute cached/empty processes next time.
      */
@@ -1224,31 +1146,6 @@
 
     private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
 
-    /**
-     * A global counter for generating sequence numbers.
-     * This value will be used when incrementing sequence numbers in individual uidRecords.
-     *
-     * Having a global counter ensures that seq numbers are monotonically increasing for a
-     * particular uid even when the uidRecord is re-created.
-     */
-    @GuardedBy("this")
-    @VisibleForTesting
-    long mProcStateSeqCounter = 0;
-
-    /**
-     * A global counter for generating sequence numbers to uniquely identify pending process starts.
-     */
-    @GuardedBy("this")
-    private long mProcStartSeqCounter = 0;
-
-    /**
-     * Contains {@link ProcessRecord} objects for pending process starts.
-     *
-     * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
-     */
-    @GuardedBy("this")
-    private final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
-
     private final Injector mInjector;
 
     static final class ProcessChangeItem {
@@ -1405,7 +1302,6 @@
 
     static final int SHOW_ERROR_UI_MSG = 1;
     static final int SHOW_NOT_RESPONDING_UI_MSG = 2;
-    static final int UPDATE_CONFIGURATION_MSG = 4;
     static final int GC_BACKGROUND_PROCESSES_MSG = 5;
     static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
     static final int SERVICE_TIMEOUT_MSG = 12;
@@ -1420,7 +1316,6 @@
     static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
     static final int REPORT_MEM_USAGE_MSG = 33;
     static final int UPDATE_TIME_PREFERENCE_MSG = 41;
-    static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
     static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
     static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
     static final int DELETE_DUMPHEAP_MSG = 51;
@@ -1434,16 +1329,10 @@
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
 
-    static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
-    static final int FIRST_COMPAT_MODE_MSG = 300;
-    static final int FIRST_SUPERVISOR_STACK_MSG = 100;
 
     static final String SERVICE_RECORD_KEY = "servicerecord";
 
-    static ServiceThread sKillThread = null;
-    static KillHandler sKillHandler = null;
-
     long mLastMemUsageReportTime = 0;
 
     /**
@@ -1477,31 +1366,7 @@
      * also corresponds to the merged configuration of the default display.
      */
     Configuration getGlobalConfiguration() {
-        return mStackSupervisor.getConfiguration();
-    }
-
-    final class KillHandler extends Handler {
-        static final int KILL_PROCESS_GROUP_MSG = 4000;
-
-        public KillHandler(Looper looper) {
-            super(looper, null, true);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case KILL_PROCESS_GROUP_MSG:
-                {
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
-                    Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                }
-                break;
-
-                default:
-                    super.handleMessage(msg);
-            }
-        }
+        return mActivityTaskManager.getGlobalConfiguration();
     }
 
     final class UiHandler extends Handler {
@@ -1600,11 +1465,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-            case UPDATE_CONFIGURATION_MSG: {
-                final ContentResolver resolver = mContext.getContentResolver();
-                Settings.System.putConfigurationForUser(resolver, (Configuration) msg.obj,
-                        msg.arg1);
-            } break;
             case GC_BACKGROUND_PROCESSES_MSG: {
                 synchronized (ActivityManagerService.this) {
                     performAppGcsIfAppropriateLocked();
@@ -1622,8 +1482,8 @@
             } break;
             case UPDATE_TIME_ZONE: {
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
+                    for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                        ProcessRecord r = mProcessList.mLruProcesses.get(i);
                         if (r.thread != null) {
                             try {
                                 r.thread.updateTimeZone();
@@ -1636,16 +1496,7 @@
             } break;
             case CLEAR_DNS_CACHE_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
-                            try {
-                                r.thread.clearDnsCache();
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.clearAllDnsCacheLocked();
                 }
             } break;
             case UPDATE_HTTP_PROXY_MSG: {
@@ -1661,19 +1512,7 @@
                     pacFileUrl = proxy.getPacFileUrl();
                 }
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        // Don't dispatch to isolated processes as they can't access
-                        // ConnectivityManager and don't have network privileges anyway.
-                        if (r.thread != null && !r.isolated) {
-                            try {
-                                r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to update http proxy for: " +
-                                        r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.setAllHttpProxyLocked(host, port, exclList, pacFileUrl);
                 }
             } break;
             case PROC_START_TIMEOUT_MSG: {
@@ -1721,29 +1560,7 @@
                 // The user's time format preference might have changed.
                 // For convenience we re-use the Intent extra values.
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
-                            try {
-                                r.thread.updateTimePrefs(msg.arg1);
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to update preferences for: "
-                                        + r.info.processName);
-                            }
-                        }
-                    }
-                }
-                break;
-            }
-            case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
-                try {
-                    Locale l = (Locale) msg.obj;
-                    IBinder service = ServiceManager.getService("mount");
-                    IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
-                    Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
-                    storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error storing locale for decryption UI", e);
+                    mProcessList.updateAllTimePrefsLocked(msg.arg1);
                 }
                 break;
             }
@@ -1867,17 +1684,7 @@
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
-                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                        ProcessRecord r = mLruProcesses.get(i);
-                        if (r.thread != null) {
-                            try {
-                                r.thread.handleTrustStorageUpdate();
-                            } catch (RemoteException ex) {
-                                Slog.w(TAG, "Failed to handle trust storage update for: " +
-                                        r.info.processName);
-                            }
-                        }
-                    }
+                    mProcessList.handleAllTrustStorageUpdateLocked();
                 }
             } break;
             }
@@ -2017,7 +1824,9 @@
             mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
 
             synchronized (this) {
-                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
+                ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
+                        false,
+                        0);
                 app.setPersistent(true);
                 app.pid = MY_PID;
                 app.getWindowProcessController().setPid(MY_PID);
@@ -2026,7 +1835,7 @@
                 synchronized (mPidsSelfLocked) {
                     mPidsSelfLocked.put(app.pid, app);
                 }
-                updateLruProcessLocked(app, false, null);
+                mProcessList.updateLruProcessLocked(app, false, null);
                 updateOomAdjLocked();
             }
         } catch (PackageManager.NameNotFoundException e) {
@@ -2052,12 +1861,12 @@
         synchronized (this) {
             mWindowManager = wm;
             mActivityTaskManager.setWindowManager(wm);
-            mStackSupervisor.setWindowManager(wm);
         }
     }
 
     public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
         mUsageStatsService = usageStatsManager;
+        mActivityTaskManager.setUsageStatsManager(usageStatsManager);
     }
 
     public void startObservingNativeCrashes() {
@@ -2294,25 +2103,39 @@
 
     @VisibleForTesting
     public ActivityManagerService(Injector injector) {
+        this(injector, null /* handlerThread */);
+    }
+
+    /**
+     * Provides the basic functionality for activity task related tests when a handler thread is
+     * given to initialize the dependency members.
+     */
+    @VisibleForTesting
+    ActivityManagerService(Injector injector, ServiceThread handlerThread) {
+        final boolean hasHandlerThread = handlerThread != null;
         mInjector = injector;
         mContext = mInjector.getContext();
         mUiContext = null;
-        GL_ES_VERSION = 0;
         mAppErrors = null;
-        mAppOpsService = mInjector.getAppOpsService(null, null);
+        mActiveUids = new ActiveUids(this, false /* postChangesToAtm */);
+        mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
         mBatteryStatsService = null;
-        mConstants = null;
-        mHandler = null;
-        mHandlerThread = null;
-        mIntentFirewall = null;
+        mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
+        mHandlerThread = handlerThread;
+        mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+        mIntentFirewall = hasHandlerThread
+                ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
         mProcessCpuThread = null;
         mProcessStats = null;
         mProviderMap = null;
-        mServices = null;
+        // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
+        // {@link ActivityStackSupervisor#cleanUpRemovedTaskLocked}.
+        mServices = hasHandlerThread ? new ActiveServices(this) : null;
         mSystemThread = null;
-        mUiHandler = injector.getUiHandler(null);
-        mUserController = null;
-        mPendingIntentController = null;
+        mUiHandler = injector.getUiHandler(null /* service */);
+        mUserController = hasHandlerThread ? new UserController(this) : null;
+        mPendingIntentController = hasHandlerThread
+                ? new PendingIntentController(handlerThread.getLooper(), mUserController) : null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
@@ -2345,13 +2168,7 @@
 
         mConstants = new ActivityManagerConstants(this, mHandler);
 
-        /* static; one-time init here */
-        if (sKillHandler == null) {
-            sKillThread = new ServiceThread(TAG + ":kill",
-                    THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
-            sKillThread.start();
-            sKillHandler = new KillHandler(sKillThread.getLooper());
-        }
+        mProcessList.init(this);
 
         mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                 "foreground", BROADCAST_FG_TIMEOUT, false);
@@ -2363,6 +2180,7 @@
         mServices = new ActiveServices(this);
         mProviderMap = new ProviderMap(this);
         mAppErrors = new AppErrors(mUiContext, this);
+        mActiveUids = new ActiveUids(this, true /* postChangesToAtm */);
 
         final File systemDir = SystemServiceManager.ensureSystemDir();
 
@@ -2387,9 +2205,6 @@
         mPendingIntentController = new PendingIntentController(
                 mHandlerThread.getLooper(), mUserController);
 
-        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
-            ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
-
         if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
             mUseFifoUiScheduling = true;
         }
@@ -2398,9 +2213,9 @@
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
 
         mActivityTaskManager = atm;
-        mActivityTaskManager.setActivityManagerService(this);
+        mActivityTaskManager.setActivityManagerService(this, mHandlerThread.getLooper(),
+                mIntentFirewall, mPendingIntentController);
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
-        mStackSupervisor = mActivityTaskManager.mStackSupervisor;
 
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
@@ -2504,12 +2319,13 @@
         if (code == SYSPROPS_TRANSACTION) {
             // We need to tell all apps about the system property change.
             ArrayList<IBinder> procs = new ArrayList<IBinder>();
-            synchronized(this) {
-                final int NP = mProcessNames.getMap().size();
-                for (int ip=0; ip<NP; ip++) {
-                    SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            synchronized (this) {
+                final int NP = mProcessList.mProcessNames.getMap().size();
+                for (int ip = 0; ip < NP; ip++) {
+                    SparseArray<ProcessRecord> apps =
+                            mProcessList.mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
-                    for (int ia=0; ia<NA; ia++) {
+                    for (int ia = 0; ia < NA; ia++) {
                         ProcessRecord app = apps.valueAt(ia);
                         if (app.thread != null) {
                             procs.add(app.thread.asBinder());
@@ -2759,329 +2575,21 @@
         mActivityTaskManager.unregisterTaskStackListener(listener);
     }
 
-    private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
-            String what, Object obj, ProcessRecord srcApp) {
-        app.lastActivityTime = now;
-
-        if (app.hasActivitiesOrRecentTasks()) {
-            // Don't want to touch dependent processes that are hosting activities.
-            return index;
-        }
-
-        int lrui = mLruProcesses.lastIndexOf(app);
-        if (lrui < 0) {
-            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
-                    + what + " " + obj + " from " + srcApp);
-            return index;
-        }
-
-        if (lrui >= index) {
-            // Don't want to cause this to move dependent processes *back* in the
-            // list as if they were less frequently used.
-            return index;
-        }
-
-        if (lrui >= mLruProcessActivityStart) {
-            // Don't want to touch dependent processes that are hosting activities.
-            return index;
-        }
-
-        mLruProcesses.remove(lrui);
-        if (index > 0) {
-            index--;
-        }
-        if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
-                + " in LRU list: " + app);
-        mLruProcesses.add(index, app);
-        return index;
-    }
-
-    static void killProcessGroup(int uid, int pid) {
-        if (sKillHandler != null) {
-            sKillHandler.sendMessage(
-                    sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
-        } else {
-            Slog.w(TAG, "Asked to kill process group before system bringup!");
-            Process.killProcessGroup(uid, pid);
-        }
+    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+            ProcessRecord client) {
+        mProcessList.updateLruProcessLocked(app, activityChange, client);
     }
 
     final void removeLruProcessLocked(ProcessRecord app) {
-        int lrui = mLruProcesses.lastIndexOf(app);
-        if (lrui >= 0) {
-            if (!app.killed) {
-                if (app.isPersistent()) {
-                    Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
-                } else {
-                    Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
-                    if (app.pid > 0) {
-                        killProcessQuiet(app.pid);
-                        killProcessGroup(app.uid, app.pid);
-                    } else {
-                        app.pendingStart = false;
-                    }
-                }
-            }
-            if (lrui <= mLruProcessActivityStart) {
-                mLruProcessActivityStart--;
-            }
-            if (lrui <= mLruProcessServiceStart) {
-                mLruProcessServiceStart--;
-            }
-            mLruProcesses.remove(lrui);
-        }
-    }
-
-    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
-            ProcessRecord client) {
-        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities
-                || app.treatLikeActivity;
-        final boolean hasService = false; // not impl yet. app.services.size() > 0;
-        if (!activityChange && hasActivity) {
-            // The process has activities, so we are only allowing activity-based adjustments
-            // to move it.  It should be kept in the front of the list with other
-            // processes that have activities, and we don't want those to change their
-            // order except due to activity operations.
-            return;
-        }
-
-        mLruSeq++;
-        final long now = SystemClock.uptimeMillis();
-        app.lastActivityTime = now;
-
-        // First a quick reject: if the app is already at the position we will
-        // put it, then there is nothing to do.
-        if (hasActivity) {
-            final int N = mLruProcesses.size();
-            if (N > 0 && mLruProcesses.get(N-1) == app) {
-                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
-                return;
-            }
-        } else {
-            if (mLruProcessServiceStart > 0
-                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
-                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
-                return;
-            }
-        }
-
-        int lrui = mLruProcesses.lastIndexOf(app);
-
-        if (app.isPersistent() && lrui >= 0) {
-            // We don't care about the position of persistent processes, as long as
-            // they are in the list.
-            if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
-            return;
-        }
-
-        /* In progress: compute new position first, so we can avoid doing work
-           if the process is not actually going to move.  Not yet working.
-        int addIndex;
-        int nextIndex;
-        boolean inActivity = false, inService = false;
-        if (hasActivity) {
-            // Process has activities, put it at the very tipsy-top.
-            addIndex = mLruProcesses.size();
-            nextIndex = mLruProcessServiceStart;
-            inActivity = true;
-        } else if (hasService) {
-            // Process has services, put it at the top of the service list.
-            addIndex = mLruProcessActivityStart;
-            nextIndex = mLruProcessServiceStart;
-            inActivity = true;
-            inService = true;
-        } else  {
-            // Process not otherwise of interest, it goes to the top of the non-service area.
-            addIndex = mLruProcessServiceStart;
-            if (client != null) {
-                int clientIndex = mLruProcesses.lastIndexOf(client);
-                if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
-                        + app);
-                if (clientIndex >= 0 && addIndex > clientIndex) {
-                    addIndex = clientIndex;
-                }
-            }
-            nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
-        }
-
-        Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
-                + mLruProcessActivityStart + "): " + app);
-        */
-
-        if (lrui >= 0) {
-            if (lrui < mLruProcessActivityStart) {
-                mLruProcessActivityStart--;
-            }
-            if (lrui < mLruProcessServiceStart) {
-                mLruProcessServiceStart--;
-            }
-            /*
-            if (addIndex > lrui) {
-                addIndex--;
-            }
-            if (nextIndex > lrui) {
-                nextIndex--;
-            }
-            */
-            mLruProcesses.remove(lrui);
-        }
-
-        /*
-        mLruProcesses.add(addIndex, app);
-        if (inActivity) {
-            mLruProcessActivityStart++;
-        }
-        if (inService) {
-            mLruProcessActivityStart++;
-        }
-        */
-
-        int nextIndex;
-        if (hasActivity) {
-            final int N = mLruProcesses.size();
-            if ((!app.hasActivities() || app.hasRecentTasks())
-                    && mLruProcessActivityStart < (N - 1)) {
-                // Process doesn't have activities, but has clients with
-                // activities...  move it up, but one below the top (the top
-                // should always have a real activity).
-                if (DEBUG_LRU) Slog.d(TAG_LRU,
-                        "Adding to second-top of LRU activity list: " + app);
-                mLruProcesses.add(N - 1, app);
-                // To keep it from spamming the LRU list (by making a bunch of clients),
-                // we will push down any other entries owned by the app.
-                final int uid = app.info.uid;
-                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
-                    ProcessRecord subProc = mLruProcesses.get(i);
-                    if (subProc.info.uid == uid) {
-                        // We want to push this one down the list.  If the process after
-                        // it is for the same uid, however, don't do so, because we don't
-                        // want them internally to be re-ordered.
-                        if (mLruProcesses.get(i - 1).info.uid != uid) {
-                            if (DEBUG_LRU) Slog.d(TAG_LRU,
-                                    "Pushing uid " + uid + " swapping at " + i + ": "
-                                    + mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
-                            ProcessRecord tmp = mLruProcesses.get(i);
-                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
-                            mLruProcesses.set(i - 1, tmp);
-                            i--;
-                        }
-                    } else {
-                        // A gap, we can stop here.
-                        break;
-                    }
-                }
-            } else {
-                // Process has activities, put it at the very tipsy-top.
-                if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
-                mLruProcesses.add(app);
-            }
-            nextIndex = mLruProcessServiceStart;
-        } else if (hasService) {
-            // Process has services, put it at the top of the service list.
-            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
-            mLruProcesses.add(mLruProcessActivityStart, app);
-            nextIndex = mLruProcessServiceStart;
-            mLruProcessActivityStart++;
-        } else  {
-            // Process not otherwise of interest, it goes to the top of the non-service area.
-            int index = mLruProcessServiceStart;
-            if (client != null) {
-                // If there is a client, don't allow the process to be moved up higher
-                // in the list than that client.
-                int clientIndex = mLruProcesses.lastIndexOf(client);
-                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
-                        + " when updating " + app);
-                if (clientIndex <= lrui) {
-                    // Don't allow the client index restriction to push it down farther in the
-                    // list than it already is.
-                    clientIndex = lrui;
-                }
-                if (clientIndex >= 0 && index > clientIndex) {
-                    index = clientIndex;
-                }
-            }
-            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
-            mLruProcesses.add(index, app);
-            nextIndex = index-1;
-            mLruProcessActivityStart++;
-            mLruProcessServiceStart++;
-        }
-
-        // If the app is currently using a content provider or service,
-        // bump those processes as well.
-        for (int j=app.connections.size()-1; j>=0; j--) {
-            ConnectionRecord cr = app.connections.valueAt(j);
-            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
-                    && cr.binding.service.app != null
-                    && cr.binding.service.app.lruSeq != mLruSeq
-                    && !cr.binding.service.app.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
-                        "service connection", cr, app);
-            }
-        }
-        for (int j=app.conProviders.size()-1; j>=0; j--) {
-            ContentProviderRecord cpr = app.conProviders.get(j).provider;
-            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
-                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
-                        "provider reference", cpr, app);
-            }
-        }
+        mProcessList.removeLruProcessLocked(app);
     }
 
     final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
-        if (uid == SYSTEM_UID) {
-            // The system gets to run in any process.  If there are multiple
-            // processes with the same uid, just pick the first (this
-            // should never happen).
-            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
-            if (procs == null) return null;
-            final int procCount = procs.size();
-            for (int i = 0; i < procCount; i++) {
-                final int procUid = procs.keyAt(i);
-                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
-                    // Don't use an app process or different user process for system component.
-                    continue;
-                }
-                return procs.valueAt(i);
-            }
-        }
-        ProcessRecord proc = mProcessNames.get(processName, uid);
-        if (false && proc != null && !keepIfLarge
-                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
-                && proc.lastCachedPss >= 4000) {
-            // Turn this condition on to cause killing to happen regularly, for testing.
-            if (proc.baseProcessTracker != null) {
-                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
-                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-                    StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
-                            proc.info.uid,
-                            holder.state.getName(),
-                            holder.state.getPackage(),
-                            proc.lastCachedPss, holder.appVersion);
-                }
-            }
-            proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
-        } else if (proc != null && !keepIfLarge
-                && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
-                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
-            if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
-                if (proc.baseProcessTracker != null) {
-                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
-                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
-                        StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
-                                proc.info.uid,
-                                holder.state.getName(),
-                                holder.state.getPackage(),
-                                proc.lastCachedPss, holder.appVersion);
-                    }
-                }
-                proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
-            }
-        }
-        return proc;
+        return mProcessList.getProcessRecordLocked(processName, uid, keepIfLarge);
+    }
+
+    final ProcessMap<ProcessRecord> getProcessNames() {
+        return mProcessList.mProcessNames;
     }
 
     void notifyPackageUse(String packageName, int reason) {
@@ -3108,7 +2616,7 @@
             info.packageName = "android";
             info.seInfoUser = SELinuxUtil.COMPLETE_STR;
             info.targetSdkVersion = Build.VERSION.SDK_INT;
-            ProcessRecord proc = startProcessLocked(processName, info /* info */,
+            ProcessRecord proc = mProcessList.startProcessLocked(processName, info /* info */,
                     false /* knownToBeDead */, 0 /* intentFlags */, ""  /* hostingType */,
                     null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
                     uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
@@ -3122,548 +2630,17 @@
             ApplicationInfo info, boolean knownToBeDead, int intentFlags,
             String hostingType, ComponentName hostingName, boolean allowWhileBooting,
             boolean isolated, boolean keepIfLarge) {
-        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
+        return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
+                hostingType,
                 hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
                 null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
                 null /* crashHandler */);
     }
 
-    @GuardedBy("this")
-    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
-            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
-            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
-            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
-        long startTime = SystemClock.elapsedRealtime();
-        ProcessRecord app;
-        if (!isolated) {
-            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
-            checkTime(startTime, "startProcess: after getProcessRecord");
-
-            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
-                // If we are in the background, then check to see if this process
-                // is bad.  If so, we will just silently fail.
-                if (mAppErrors.isBadProcessLocked(info)) {
-                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
-                            + "/" + info.processName);
-                    return null;
-                }
-            } else {
-                // When the user is explicitly starting a process, then clear its
-                // crash count so that we won't make it bad until they see at
-                // least one crash dialog again, and make the process good again
-                // if it had been bad.
-                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
-                        + "/" + info.processName);
-                mAppErrors.resetProcessCrashTimeLocked(info);
-                if (mAppErrors.isBadProcessLocked(info)) {
-                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
-                            UserHandle.getUserId(info.uid), info.uid,
-                            info.processName);
-                    mAppErrors.clearBadProcessLocked(info);
-                    if (app != null) {
-                        app.bad = false;
-                    }
-                }
-            }
-        } else {
-            // If this is an isolated process, it can't re-use an existing process.
-            app = null;
-        }
-
-        // We don't have to do anything more if:
-        // (1) There is an existing application record; and
-        // (2) The caller doesn't think it is dead, OR there is no thread
-        //     object attached to it so we know it couldn't have crashed; and
-        // (3) There is a pid assigned to it, so it is either starting or
-        //     already running.
-        if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
-                + " app=" + app + " knownToBeDead=" + knownToBeDead
-                + " thread=" + (app != null ? app.thread : null)
-                + " pid=" + (app != null ? app.pid : -1));
-        if (app != null && app.pid > 0) {
-            if ((!knownToBeDead && !app.killed) || app.thread == null) {
-                // We already have the app running, or are waiting for it to
-                // come up (we have a pid but not yet its thread), so keep it.
-                if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
-                // If this is a new package in the process, add the package to the list
-                app.addPackage(info.packageName, info.versionCode, mProcessStats);
-                checkTime(startTime, "startProcess: done, added package to proc");
-                return app;
-            }
-
-            // An application record is attached to a previous process,
-            // clean it up now.
-            if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
-            checkTime(startTime, "startProcess: bad proc running, killing");
-            killProcessGroup(app.uid, app.pid);
-            handleAppDiedLocked(app, true, true);
-            checkTime(startTime, "startProcess: done killing old proc");
-        }
-
-        String hostingNameStr = hostingName != null
-                ? hostingName.flattenToShortString() : null;
-
-        if (app == null) {
-            checkTime(startTime, "startProcess: creating new process record");
-            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
-            if (app == null) {
-                Slog.w(TAG, "Failed making new process record for "
-                        + processName + "/" + info.uid + " isolated=" + isolated);
-                return null;
-            }
-            app.crashHandler = crashHandler;
-            app.isolatedEntryPoint = entryPoint;
-            app.isolatedEntryPointArgs = entryPointArgs;
-            checkTime(startTime, "startProcess: done creating new process record");
-        } else {
-            // If this is a new package in the process, add the package to the list
-            app.addPackage(info.packageName, info.versionCode, mProcessStats);
-            checkTime(startTime, "startProcess: added package to existing proc");
-        }
-
-        // If the system is not ready yet, then hold off on starting this
-        // process until it is.
-        if (!mProcessesReady
-                && !isAllowedWhileBooting(info)
-                && !allowWhileBooting) {
-            if (!mProcessesOnHold.contains(app)) {
-                mProcessesOnHold.add(app);
-            }
-            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
-                    "System not ready, putting on hold: " + app);
-            checkTime(startTime, "startProcess: returning with proc on hold");
-            return app;
-        }
-
-        checkTime(startTime, "startProcess: stepping in to startProcess");
-        final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
-        checkTime(startTime, "startProcess: done starting proc!");
-        return success ? app : null;
-    }
-
     boolean isAllowedWhileBooting(ApplicationInfo ai) {
         return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
     }
 
-    @GuardedBy("this")
-    private final void startProcessLocked(ProcessRecord app,
-            String hostingType, String hostingNameStr) {
-        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
-    }
-
-    @GuardedBy("this")
-    private final boolean startProcessLocked(ProcessRecord app,
-            String hostingType, String hostingNameStr, String abiOverride) {
-        return startProcessLocked(app, hostingType, hostingNameStr,
-                false /* disableHiddenApiChecks */, abiOverride);
-    }
-
-    /**
-     * @return {@code true} if process start is successful, false otherwise.
-     */
-    @GuardedBy("this")
-    private final boolean startProcessLocked(ProcessRecord app, String hostingType,
-            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
-        if (app.pendingStart) {
-            return true;
-        }
-        long startTime = SystemClock.elapsedRealtime();
-        if (app.pid > 0 && app.pid != MY_PID) {
-            checkTime(startTime, "startProcess: removing from pids map");
-            synchronized (mPidsSelfLocked) {
-                mPidsSelfLocked.remove(app.pid);
-                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-            }
-            checkTime(startTime, "startProcess: done removing from pids map");
-            app.setPid(0);
-        }
-
-        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
-                "startProcessLocked removing on hold: " + app);
-        mProcessesOnHold.remove(app);
-
-        checkTime(startTime, "startProcess: starting to update cpu stats");
-        updateCpuStats();
-        checkTime(startTime, "startProcess: done updating cpu stats");
-
-        try {
-            try {
-                final int userId = UserHandle.getUserId(app.uid);
-                AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
-            } catch (RemoteException e) {
-                throw e.rethrowAsRuntimeException();
-            }
-
-            int uid = app.uid;
-            int[] gids = null;
-            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
-            if (!app.isolated) {
-                int[] permGids = null;
-                try {
-                    checkTime(startTime, "startProcess: getting gids from package manager");
-                    final IPackageManager pm = AppGlobals.getPackageManager();
-                    permGids = pm.getPackageGids(app.info.packageName,
-                            MATCH_DEBUG_TRIAGED_MISSING, app.userId);
-                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                            StorageManagerInternal.class);
-                    mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
-                            app.info.packageName);
-                } catch (RemoteException e) {
-                    throw e.rethrowAsRuntimeException();
-                }
-
-                /*
-                 * Add shared application and profile GIDs so applications can share some
-                 * resources like shared libraries and access user-wide resources
-                 */
-                if (ArrayUtils.isEmpty(permGids)) {
-                    gids = new int[3];
-                } else {
-                    gids = new int[permGids.length + 3];
-                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
-                }
-                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
-                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
-                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
-
-                // Replace any invalid GIDs
-                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
-                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
-            }
-            checkTime(startTime, "startProcess: building args");
-            if (mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
-                uid = 0;
-            }
-            int runtimeFlags = 0;
-            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
-                runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
-                // Also turn on CheckJNI for debuggable apps. It's quite
-                // awkward to turn on otherwise.
-                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
-            }
-            // Run the app in safe mode if its manifest requests so or the
-            // system is booted in safe mode.
-            if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
-                mSafeMode == true) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
-            }
-            if ("1".equals(SystemProperties.get("debug.checkjni"))) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
-            }
-            String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
-            if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
-                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
-            }
-            String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
-            if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
-                runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
-            }
-            if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
-            }
-            if ("1".equals(SystemProperties.get("debug.assert"))) {
-                runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
-            }
-            if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
-                // Enable all debug flags required by the native debugger.
-                runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
-                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
-                runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
-                mNativeDebuggingApp = null;
-            }
-
-            if (app.info.isPrivilegedApp() &&
-                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
-                runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
-            }
-
-            if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
-                app.info.maybeUpdateHiddenApiEnforcementPolicy(
-                        mHiddenApiBlacklist.getPolicyForPrePApps(),
-                        mHiddenApiBlacklist.getPolicyForPApps());
-                @HiddenApiEnforcementPolicy int policy =
-                        app.info.getHiddenApiEnforcementPolicy();
-                int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
-                if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
-                    throw new IllegalStateException("Invalid API policy: " + policy);
-                }
-                runtimeFlags |= policyBits;
-            }
-
-            String invokeWith = null;
-            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                // Debuggable apps may include a wrapper script with their library directory.
-                String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
-                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
-                try {
-                    if (new File(wrapperFileName).exists()) {
-                        invokeWith = "/system/bin/logwrapper " + wrapperFileName;
-                    }
-                } finally {
-                    StrictMode.setThreadPolicy(oldPolicy);
-                }
-            }
-
-            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
-            if (requiredAbi == null) {
-                requiredAbi = Build.SUPPORTED_ABIS[0];
-            }
-
-            String instructionSet = null;
-            if (app.info.primaryCpuAbi != null) {
-                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
-            }
-
-            app.gids = gids;
-            app.setRequiredAbi(requiredAbi);
-            app.instructionSet = instructionSet;
-
-            // the per-user SELinux context must be set
-            if (TextUtils.isEmpty(app.info.seInfoUser)) {
-                Slog.wtf(TAG, "SELinux tag not defined",
-                        new IllegalStateException("SELinux tag not defined for "
-                        + app.info.packageName + " (uid " + app.uid + ")"));
-            }
-            final String seInfo = app.info.seInfo
-                    + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
-            // Start the process.  It will either succeed and return a result containing
-            // the PID of the new process, or else throw a RuntimeException.
-            final String entryPoint = "android.app.ActivityThread";
-
-            return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
-                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
-                    startTime);
-        } catch (RuntimeException e) {
-            Slog.e(TAG, "Failure starting process " + app.processName, e);
-
-            // Something went very wrong while trying to start this process; one
-            // common case is when the package is frozen due to an active
-            // upgrade. To recover, clean up any active bookkeeping related to
-            // starting this process. (We already invoked this method once when
-            // the package was initially frozen through KILL_APPLICATION_MSG, so
-            // it doesn't hurt to use it again.)
-            forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
-                    false, true, false, false, app.userId, "start failure");
-            return false;
-        }
-    }
-
-    @GuardedBy("this")
-    private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
-            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
-            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
-            long startTime) {
-        app.pendingStart = true;
-        app.killedByAm = false;
-        app.removed = false;
-        app.killed = false;
-        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
-        app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
-        if (mConstants.FLAG_PROCESS_START_ASYNC) {
-            if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
-                    "Posting procStart msg for " + app.toShortString());
-            mProcStartHandler.post(() -> {
-                try {
-                    synchronized (ActivityManagerService.this) {
-                        final String reason = isProcStartValidLocked(app, startSeq);
-                        if (reason != null) {
-                            Slog.w(TAG_PROCESSES, app + " not valid anymore,"
-                                    + " don't start process, " + reason);
-                            app.pendingStart = false;
-                            return;
-                        }
-                        app.setUsingWrapper(invokeWith != null
-                                || SystemProperties.get("wrap." + app.processName) != null);
-                        mPendingStarts.put(startSeq, app);
-                    }
-                    final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
-                            app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
-                            requiredAbi, instructionSet, invokeWith, app.startTime);
-                    synchronized (ActivityManagerService.this) {
-                        handleProcessStartedLocked(app, startResult, startSeq);
-                    }
-                } catch (RuntimeException e) {
-                    synchronized (ActivityManagerService.this) {
-                        Slog.e(TAG, "Failure starting process " + app.processName, e);
-                        mPendingStarts.remove(startSeq);
-                        app.pendingStart = false;
-                        forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
-                                false, false, true, false, false, app.userId, "start failure");
-                    }
-                }
-            });
-            return true;
-        } else {
-            try {
-                final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
-                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
-                        invokeWith, startTime);
-                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
-                        startSeq, false);
-            } catch (RuntimeException e) {
-                Slog.e(TAG, "Failure starting process " + app.processName, e);
-                app.pendingStart = false;
-                forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
-                        false, false, true, false, false, app.userId, "start failure");
-            }
-            return app.pid > 0;
-        }
-    }
-
-    private ProcessStartResult startProcess(String hostingType, String entryPoint,
-            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
-            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
-            long startTime) {
-        try {
-            final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
-            final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
-                    .getVisibleVolumesForUser(UserHandle.getUserId(uid));
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
-                    app.processName);
-            checkTime(startTime, "startProcess: asking zygote to start proc");
-            final ProcessStartResult startResult;
-            if (hostingType.equals("webview_service")) {
-                startResult = startWebView(entryPoint,
-                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
-                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null, app.info.packageName,
-                        packageNames, visibleVolIds,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
-            } else {
-                startResult = Process.start(entryPoint,
-                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
-                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, invokeWith, app.info.packageName,
-                        packageNames, visibleVolIds,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
-            }
-            checkTime(startTime, "startProcess: returned from zygote!");
-            return startResult;
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    @GuardedBy("this")
-    private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
-        StringBuilder sb = null;
-        if (app.killedByAm) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("killedByAm=true;");
-        }
-        if (mProcessNames.get(app.processName, app.uid) != app) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("No entry in mProcessNames;");
-        }
-        if (!app.pendingStart) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("pendingStart=false;");
-        }
-        if (app.startSeq > expectedStartSeq) {
-            if (sb == null) sb = new StringBuilder();
-            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
-        }
-        return sb == null ? null : sb.toString();
-    }
-
-    @GuardedBy("this")
-    private boolean handleProcessStartedLocked(ProcessRecord pending,
-            ProcessStartResult startResult, long expectedStartSeq) {
-        // Indicates that this process start has been taken care of.
-        if (mPendingStarts.get(expectedStartSeq) == null) {
-            if (pending.pid == startResult.pid) {
-                pending.setUsingWrapper(startResult.usingWrapper);
-                // TODO: Update already existing clients of usingWrapper
-            }
-            return false;
-        }
-        return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
-                expectedStartSeq, false);
-    }
-
-    @GuardedBy("this")
-    private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
-            long expectedStartSeq, boolean procAttached) {
-        mPendingStarts.remove(expectedStartSeq);
-        final String reason = isProcStartValidLocked(app, expectedStartSeq);
-        if (reason != null) {
-            Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
-                    + ", " + reason);
-            app.pendingStart = false;
-            Process.killProcessQuiet(pid);
-            Process.killProcessGroup(app.uid, app.pid);
-            return false;
-        }
-        mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
-        checkTime(app.startTime, "startProcess: done updating battery stats");
-
-        EventLog.writeEvent(EventLogTags.AM_PROC_START,
-                UserHandle.getUserId(app.startUid), pid, app.startUid,
-                app.processName, app.hostingType,
-                app.hostingNameStr != null ? app.hostingNameStr : "");
-
-        try {
-            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
-                    app.seInfo, app.info.sourceDir, pid);
-        } catch (RemoteException ex) {
-            // Ignore
-        }
-
-        if (app.isPersistent()) {
-            Watchdog.getInstance().processStarted(app.processName, pid);
-        }
-
-        checkTime(app.startTime, "startProcess: building log message");
-        StringBuilder buf = mStringBuilder;
-        buf.setLength(0);
-        buf.append("Start proc ");
-        buf.append(pid);
-        buf.append(':');
-        buf.append(app.processName);
-        buf.append('/');
-        UserHandle.formatUid(buf, app.startUid);
-        if (app.isolatedEntryPoint != null) {
-            buf.append(" [");
-            buf.append(app.isolatedEntryPoint);
-            buf.append("]");
-        }
-        buf.append(" for ");
-        buf.append(app.hostingType);
-        if (app.hostingNameStr != null) {
-            buf.append(" ");
-            buf.append(app.hostingNameStr);
-        }
-        reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
-        app.setPid(pid);
-        app.setUsingWrapper(usingWrapper);
-        app.pendingStart = false;
-        checkTime(app.startTime, "startProcess: starting to update pids map");
-        ProcessRecord oldApp;
-        synchronized (mPidsSelfLocked) {
-            oldApp = mPidsSelfLocked.get(pid);
-        }
-        // If there is already an app occupying that pid that hasn't been cleaned up
-        if (oldApp != null && !app.isolated) {
-            // Clean up anything relating to this pid first
-            Slog.w(TAG, "Reusing pid " + pid
-                    + " while app is still mapped to it");
-            cleanUpApplicationRecordLocked(oldApp, false, false, -1,
-                    true /*replacingPid*/);
-        }
-        synchronized (mPidsSelfLocked) {
-            this.mPidsSelfLocked.put(pid, app);
-            if (!procAttached) {
-                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
-                msg.obj = app;
-                mHandler.sendMessageDelayed(msg, usingWrapper
-                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
-            }
-        }
-        checkTime(app.startTime, "startProcess: done updating pids map");
-        return true;
-    }
-
     void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
         if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
                 "updateUsageStats: comp=" + activity + "res=" + resumed);
@@ -3693,14 +2670,6 @@
         }
     }
 
-    boolean getCheckedForSetup() {
-        return mCheckedForSetup;
-    }
-
-    void setCheckedForSetup(boolean checked) {
-        mCheckedForSetup = checked;
-    }
-
     CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
         return mAtmInternal.compatibilityInfoForPackage(ai);
     }
@@ -3733,10 +2702,10 @@
                     "getPackageProcessState");
         }
 
-        int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+        int procState = PROCESS_STATE_NONEXISTENT;
         synchronized (this) {
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                final ProcessRecord proc = mLruProcesses.get(i);
+            for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+                final ProcessRecord proc = mProcessList.mLruProcesses.get(i);
                 if (procState > proc.setProcState) {
                     if (proc.pkgList.containsKey(packageName) ||
                             (proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
@@ -3764,7 +2733,7 @@
                         "Unable to set a higher trim level than current level");
             }
             if (!(level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN ||
-                    app.curProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
+                    app.getCurProcState() > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
                 throw new IllegalArgumentException("Unable to set a background trim level "
                     + "on a foreground process");
             }
@@ -3869,7 +2838,7 @@
                 } else {
                     UidRecord validateUid = mValidateUids.get(item.uid);
                     if (validateUid == null) {
-                        validateUid = new UidRecord(item.uid);
+                        validateUid = new UidRecord(item.uid, mAtmInternal);
                         mValidateUids.put(item.uid, validateUid);
                     }
                     if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
@@ -3877,7 +2846,7 @@
                     } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
                         validateUid.idle = false;
                     }
-                    validateUid.curProcState = validateUid.setProcState = item.processState;
+                    validateUid.setCurProcState(validateUid.setProcState = item.processState);
                     validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
                 }
             }
@@ -3953,8 +2922,7 @@
                                 final boolean newAboveCut = item.processState <= reg.cutpoint;
                                 doReport = lastAboveCut != newAboveCut;
                             } else {
-                                doReport = item.processState
-                                        != ActivityManager.PROCESS_STATE_NONEXISTENT;
+                                doReport = item.processState != PROCESS_STATE_NONEXISTENT;
                             }
                         }
                         if (doReport) {
@@ -4030,9 +2998,35 @@
     public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
-                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
-                userId);
+        synchronized (this) {
+            /**
+             * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+             * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+             * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+             * later to modify process settings due to the flags.
+             * TODO(b/80414790): Investigate a better way of untangling this.
+             */
+            return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
+                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+                    bOptions, userId);
+        }
+    }
+
+    WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        synchronized (this) {
+            /**
+             * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this
+             * call when called/invoked from the shell command. To avoid deadlock, we go ahead and
+             * acquire the AMS lock now since ATMS will need to synchronously call back into AMS
+             * later to modify process settings due to the flags.
+             * TODO(b/80414790): Investigate a better way of untangling this.
+             */
+            return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent,
+                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+                    bOptions, userId);
+        }
     }
 
     @Override
@@ -4111,7 +3105,7 @@
      * to the process.
      */
     @GuardedBy("this")
-    private final void handleAppDiedLocked(ProcessRecord app,
+    final void handleAppDiedLocked(ProcessRecord app,
             boolean restarting, boolean allowRestart) {
         int pid = app.pid;
         boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
@@ -4127,45 +3121,13 @@
             clearProfilerLocked();
         }
 
-        // Remove this application's activities from active lists.
-        boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app.getWindowProcessController());
-
-        app.clearRecentTasks();
-        app.clearActivities();
-
-        if (app.getActiveInstrumentation() != null) {
+        mAtmInternal.handleAppDied(app.getWindowProcessController(), restarting, () -> {
             Slog.w(TAG, "Crash of app " + app.processName
-                  + " running instrumentation " + app.getActiveInstrumentation().mClass);
+                    + " running instrumentation " + app.getActiveInstrumentation().mClass);
             Bundle info = new Bundle();
             info.putString("shortMsg", "Process crashed.");
             finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
-        }
-
-        mWindowManager.deferSurfaceLayout();
-        try {
-            if (!restarting && hasVisibleActivities
-                    && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
-                // If there was nothing to resume, and we are not already restarting this process, but
-                // there is a visible activity that is hosted by the process...  then make sure all
-                // visible activities are running, taking care of restarting this process.
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-
-    }
-
-    private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
-        final IBinder threadBinder = thread.asBinder();
-        // Find the application record.
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
-            final ProcessRecord rec = mLruProcesses.get(i);
-            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
-                return i;
-            }
-        }
-        return -1;
+        });
     }
 
     ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
@@ -4173,15 +3135,14 @@
             return null;
         }
 
-        int appIndex = getLRURecordIndexForAppLocked(thread);
-        if (appIndex >= 0) {
-            return mLruProcesses.get(appIndex);
-        }
+        ProcessRecord record = mProcessList.getLRURecordForAppLocked(thread);
+        if (record != null) return record;
 
         // Validation: if it isn't in the LRU list, it shouldn't exist, but let's
         // double-check that.
         final IBinder threadBinder = thread.asBinder();
-        final ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+        final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+                mProcessList.mProcessNames.getMap();
         for (int i = pmap.size()-1; i >= 0; i--) {
             final SparseArray<ProcessRecord> procs = pmap.valueAt(i);
             for (int j = procs.size()-1; j >= 0; j--) {
@@ -4201,17 +3162,7 @@
         // If there are no longer any background processes running,
         // and the app that died was not running instrumentation,
         // then tell everyone we are now low on memory.
-        boolean haveBg = false;
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
-            ProcessRecord rec = mLruProcesses.get(i);
-            if (rec.thread != null
-                    && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
-                haveBg = true;
-                break;
-            }
-        }
-
-        if (!haveBg) {
+        if (!mProcessList.haveBackgroundProcessLocked()) {
             boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
             if (doReport) {
                 long now = SystemClock.uptimeMillis();
@@ -4222,11 +3173,12 @@
                 }
             }
             final ArrayList<ProcessMemInfo> memInfos
-                    = doReport ? new ArrayList<ProcessMemInfo>(mLruProcesses.size()) : null;
-            EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
+                    = doReport ? new ArrayList<ProcessMemInfo>(mProcessList.getLruSizeLocked())
+                    : null;
+            EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mProcessList.getLruSizeLocked());
             long now = SystemClock.uptimeMillis();
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord rec = mLruProcesses.get(i);
+            for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                ProcessRecord rec = mProcessList.mLruProcesses.get(i);
                 if (rec == dyingProc || rec.thread == null) {
                     continue;
                 }
@@ -4283,7 +3235,7 @@
             if (!fromBinderDied) {
                 killProcessQuiet(pid);
             }
-            killProcessGroup(app.uid, pid);
+            ProcessList.killProcessGroup(app.uid, pid);
             app.killed = true;
         }
 
@@ -4702,7 +3654,7 @@
                     return;
                 }
                 synchronized (this) {
-                    killPackageProcessesLocked(packageName, appId, targetUserId,
+                    mProcessList.killPackageProcessesLocked(packageName, appId, targetUserId,
                             ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
                 }
             }
@@ -4725,30 +3677,7 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessNames.getMap().size();
-                for (int ip = 0; ip < NP; ip++) {
-                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-                    final int NA = apps.size();
-                    for (int ia = 0; ia < NA; ia++) {
-                        final ProcessRecord app = apps.valueAt(ia);
-                        if (app.isPersistent()) {
-                            // We don't kill persistent processes.
-                            continue;
-                        }
-                        if (app.removed) {
-                            procs.add(app);
-                        } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
-                            app.removed = true;
-                            procs.add(app);
-                        }
-                    }
-                }
-
-                final int N = procs.size();
-                for (int i = 0; i < N; i++) {
-                    removeProcessLocked(procs.get(i), false, true, "kill all background");
-                }
+                mProcessList.killAllBackgroundProcessesLocked();
 
                 mAllowLowerMemLevel = true;
 
@@ -4782,27 +3711,7 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessNames.getMap().size();
-                for (int ip = 0; ip < NP; ip++) {
-                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-                    final int NA = apps.size();
-                    for (int ia = 0; ia < NA; ia++) {
-                        final ProcessRecord app = apps.valueAt(ia);
-                        if (app.removed) {
-                            procs.add(app);
-                        } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
-                                && (maxProcState < 0 || app.setProcState > maxProcState)) {
-                            app.removed = true;
-                            procs.add(app);
-                        }
-                    }
-                }
-
-                final int N = procs.size();
-                for (int i = 0; i < N; i++) {
-                    removeProcessLocked(procs.get(i), false, true, "kill all background except");
-                }
+                mProcessList.killAllBackgroundProcessesExceptLocked(minTargetSdk, maxProcState);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -4919,48 +3828,7 @@
 
     @Override
     public void closeSystemDialogs(String reason) {
-        enforceNotIsolatedCaller("closeSystemDialogs");
-
-        final int pid = Binder.getCallingPid();
-        final int uid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                // Only allow this from foreground processes, so that background
-                // applications can't abuse it to prevent system UI from being shown.
-                if (uid >= FIRST_APPLICATION_UID) {
-                    ProcessRecord proc;
-                    synchronized (mPidsSelfLocked) {
-                        proc = mPidsSelfLocked.get(pid);
-                    }
-                    if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                        Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
-                                + " from background process " + proc);
-                        return;
-                    }
-                }
-                closeSystemDialogsLocked(reason);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @GuardedBy("this")
-    void closeSystemDialogsLocked(String reason) {
-        Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                | Intent.FLAG_RECEIVER_FOREGROUND);
-        if (reason != null) {
-            intent.putExtra("reason", reason);
-        }
-        mWindowManager.closeSystemDialogs(reason);
-
-        mStackSupervisor.closeSystemDialogsLocked();
-
-        broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                OP_NONE, null, false, false,
-                -1, SYSTEM_UID, UserHandle.USER_ALL);
+        mAtmInternal.closeSystemDialogs(reason);
     }
 
     @Override
@@ -5141,81 +4009,6 @@
                 null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
     }
 
-
-    @GuardedBy("this")
-    private final boolean killPackageProcessesLocked(String packageName, int appId,
-            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
-            boolean doit, boolean evenPersistent, String reason) {
-        ArrayList<ProcessRecord> procs = new ArrayList<>();
-
-        // Remove all processes this package may have touched: all with the
-        // same UID (except for the system or root user), and all whose name
-        // matches the package name.
-        final int NP = mProcessNames.getMap().size();
-        for (int ip=0; ip<NP; ip++) {
-            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-            final int NA = apps.size();
-            for (int ia=0; ia<NA; ia++) {
-                ProcessRecord app = apps.valueAt(ia);
-                if (app.isPersistent() && !evenPersistent) {
-                    // we don't kill persistent processes
-                    continue;
-                }
-                if (app.removed) {
-                    if (doit) {
-                        procs.add(app);
-                    }
-                    continue;
-                }
-
-                // Skip process if it doesn't meet our oom adj requirement.
-                if (app.setAdj < minOomAdj) {
-                    continue;
-                }
-
-                // If no package is specified, we call all processes under the
-                // give user id.
-                if (packageName == null) {
-                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
-                        continue;
-                    }
-                    if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
-                        continue;
-                    }
-                // Package has been specified, we want to hit all processes
-                // that match it.  We need to qualify this by the processes
-                // that are running under the specified app and user ID.
-                } else {
-                    final boolean isDep = app.pkgDeps != null
-                            && app.pkgDeps.contains(packageName);
-                    if (!isDep && UserHandle.getAppId(app.uid) != appId) {
-                        continue;
-                    }
-                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
-                        continue;
-                    }
-                    if (!app.pkgList.containsKey(packageName) && !isDep) {
-                        continue;
-                    }
-                }
-
-                // Process has passed all conditions, kill it!
-                if (!doit) {
-                    return true;
-                }
-                app.removed = true;
-                procs.add(app);
-            }
-        }
-
-        int N = procs.size();
-        for (int i=0; i<N; i++) {
-            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
-        }
-        updateOomAdjLocked();
-        return N > 0;
-    }
-
     private void cleanupDisabledPackageComponentsLocked(
             String packageName, int userId, boolean killProcess, String[] changedClasses) {
 
@@ -5275,15 +4068,8 @@
             return;
         }
 
-        // Clean-up disabled activities.
-        if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
-                packageName, disabledClasses, true, false, userId) && mBooted) {
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            mStackSupervisor.scheduleIdleLocked();
-        }
-
-        // Clean-up disabled tasks
-        mActivityTaskManager.getRecentTasks().cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
+        mAtmInternal.cleanupDisabledPackageComponents(
+                packageName, disabledClasses, userId, mBooted);
 
         // Clean-up disabled services.
         mServices.bringDownDisabledPackageServicesLocked(
@@ -5343,19 +4129,12 @@
             mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
         }
 
-        boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
+        boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
                 ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
-        didSomething |= mActivityTaskManager.getActivityStartController().clearPendingActivityLaunches(packageName);
-
-        if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
-                packageName, null, doit, evenPersistent, userId)) {
-            if (!doit) {
-                return true;
-            }
-            didSomething = true;
-        }
+        didSomething |=
+                mAtmInternal.onForceStopPackage(packageName, doit, evenPersistent, userId);
 
         if (mServices.bringDownDisabledPackageServicesLocked(
                 packageName, null, userId, evenPersistent, true, doit)) {
@@ -5405,136 +4184,17 @@
                 }
             }
             if (mBooted) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-                mStackSupervisor.scheduleIdleLocked();
+                mAtmInternal.resumeTopActivities(true /* scheduleIdle */);
             }
         }
 
         return didSomething;
     }
 
-    private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
-        return removeProcessNameLocked(name, uid, null);
-    }
-
-    private final ProcessRecord removeProcessNameLocked(final String name, final int uid,
-            final ProcessRecord expecting) {
-        ProcessRecord old = mProcessNames.get(name, uid);
-        // Only actually remove when the currently recorded value matches the
-        // record that we expected; if it doesn't match then we raced with a
-        // newly created process and we don't want to destroy the new one.
-        if ((expecting == null) || (old == expecting)) {
-            mProcessNames.remove(name, uid);
-        }
-        if (old != null && old.uidRecord != null) {
-            old.uidRecord.numProcs--;
-            if (old.uidRecord.numProcs == 0) {
-                // No more processes using this uid, tell clients it is gone.
-                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                        "No more processes in " + old.uidRecord);
-                enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
-                EventLogTags.writeAmUidStopped(uid);
-                mActiveUids.remove(uid);
-                noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
-            }
-            old.uidRecord = null;
-        }
-        mIsolatedProcesses.remove(uid);
-        return old;
-    }
-
-    private final void addProcessNameLocked(ProcessRecord proc) {
-        // We shouldn't already have a process under this name, but just in case we
-        // need to clean up whatever may be there now.
-        ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
-        if (old == proc && proc.isPersistent()) {
-            // We are re-adding a persistent process.  Whatevs!  Just leave it there.
-            Slog.w(TAG, "Re-adding persistent process " + proc);
-        } else if (old != null) {
-            Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
-        }
-        UidRecord uidRec = mActiveUids.get(proc.uid);
-        if (uidRec == null) {
-            uidRec = new UidRecord(proc.uid);
-            // This is the first appearance of the uid, report it now!
-            if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                    "Creating new process uid: " + uidRec);
-            if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
-                    || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
-                uidRec.setWhitelist = uidRec.curWhitelist = true;
-            }
-            uidRec.updateHasInternetPermission();
-            mActiveUids.put(proc.uid, uidRec);
-            EventLogTags.writeAmUidRunning(uidRec.uid);
-            noteUidProcessState(uidRec.uid, uidRec.curProcState);
-        }
-        proc.uidRecord = uidRec;
-
-        // Reset render thread tid if it was already set, so new process can set it again.
-        proc.renderThreadTid = 0;
-        uidRec.numProcs++;
-        mProcessNames.put(proc.processName, proc.uid, proc);
-        if (proc.isolated) {
-            mIsolatedProcesses.put(proc.uid, proc);
-        }
-    }
-
-    @GuardedBy("this")
-    boolean removeProcessLocked(ProcessRecord app,
-            boolean callerWillRestart, boolean allowRestart, String reason) {
-        final String name = app.processName;
-        final int uid = app.uid;
-        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
-            "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
-
-        ProcessRecord old = mProcessNames.get(name, uid);
-        if (old != app) {
-            // This process is no longer active, so nothing to do.
-            Slog.w(TAG, "Ignoring remove of inactive process: " + app);
-            return false;
-        }
-        removeProcessNameLocked(name, uid);
-        mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
-
-        boolean needRestart = false;
-        if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
-            int pid = app.pid;
-            if (pid > 0) {
-                synchronized (mPidsSelfLocked) {
-                    mPidsSelfLocked.remove(pid);
-                    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-                }
-                mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
-                if (app.isolated) {
-                    mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
-                    getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
-                }
-            }
-            boolean willRestart = false;
-            if (app.isPersistent() && !app.isolated) {
-                if (!callerWillRestart) {
-                    willRestart = true;
-                } else {
-                    needRestart = true;
-                }
-            }
-            app.kill(reason, true);
-            handleAppDiedLocked(app, willRestart, allowRestart);
-            if (willRestart) {
-                removeLruProcessLocked(app);
-                addAppLocked(app.info, null, false, null /* ABI override */);
-            }
-        } else {
-            mRemovedProcesses.add(app);
-        }
-
-        return needRestart;
-    }
-
     @GuardedBy("this")
     private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
         cleanupAppInLaunchingProvidersLocked(app, true);
-        removeProcessLocked(app, false, true, "timeout publishing content providers");
+        mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
     }
 
     private final void processStartTimedOutLocked(ProcessRecord app) {
@@ -5552,7 +4212,7 @@
             Slog.w(TAG, "Process " + app + " failed to attach");
             EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
                     pid, app.uid, app.processName);
-            removeProcessNameLocked(app.processName, app.uid);
+            mProcessList.removeProcessNameLocked(app.processName, app.uid);
             mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
             mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             // Take care of any launching providers waiting for this process.
@@ -5608,9 +4268,10 @@
         // It's possible that process called attachApplication before we got a chance to
         // update the internal state.
         if (app == null && startSeq > 0) {
-            final ProcessRecord pending = mPendingStarts.get(startSeq);
+            final ProcessRecord pending = mProcessList.mPendingStarts.get(startSeq);
             if (pending != null && pending.startUid == callingUid
-                    && handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(),
+                    && mProcessList.handleProcessStartedLocked(pending, pid, pending
+                            .isUsingWrapper(),
                             startSeq, true)) {
                 app = pending;
             }
@@ -5652,7 +4313,7 @@
             app.deathRecipient = adr;
         } catch (RemoteException e) {
             app.resetPackageList(mProcessStats);
-            startProcessLocked(app, "link fail", processName);
+            mProcessList.startProcessLocked(app, "link fail", processName);
             return false;
         }
 
@@ -5830,7 +4491,7 @@
             }
 
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
-            mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
+            mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             if (app.isolatedEntryPoint != null) {
                 // This is an isolated process which should just call an entry point instead of
@@ -5863,7 +4524,7 @@
                 profilerInfo = null;
             }
             checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
-            updateLruProcessLocked(app, false, null);
+            mProcessList.updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
@@ -5874,7 +4535,7 @@
 
             app.resetPackageList(mProcessStats);
             app.unlinkDeathRecipient();
-            startProcessLocked(app, "bind fail", processName);
+            mProcessList.startProcessLocked(app, "bind fail", processName);
             return false;
         }
 
@@ -5890,9 +4551,7 @@
         // See if the top visible activity is waiting to run in this process...
         if (normalMode) {
             try {
-                if (mStackSupervisor.attachApplicationLocked(app)) {
-                    didSomething = true;
-                }
+                didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
             } catch (Exception e) {
                 Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                 badApp = true;
@@ -6049,7 +4708,7 @@
                 for (int ip=0; ip<NP; ip++) {
                     if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
                             + procs.get(ip));
-                    startProcessLocked(procs.get(ip), "on-hold", null);
+                    mProcessList.startProcessLocked(procs.get(ip), "on-hold", null);
                 }
             }
             if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -6481,7 +5140,7 @@
             if (uidRec == null || uidRec.idle) {
                 return false;
             }
-            return uidRec.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            return uidRec.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
         }
     }
 
@@ -6495,7 +5154,7 @@
 
     int getUidStateLocked(int uid) {
         UidRecord uidRec = mActiveUids.get(uid);
-        return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+        return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState();
     }
 
     // =========================================================
@@ -6549,8 +5208,7 @@
         synchronized (mPidsSelfLocked) {
             for (int i = 0; i < pids.length; i++) {
                 ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
-                states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT :
-                        pr.curProcState;
+                states[i] = (pr == null) ? PROCESS_STATE_NONEXISTENT : pr.getCurProcState();
                 if (scores != null) {
                     scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj;
                 }
@@ -6834,7 +5492,7 @@
                             proc = mPidsSelfLocked.get(callingPid);
                         }
                         if (proc != null &&
-                                !ActivityManager.isProcStateBackground(proc.curProcState)) {
+                                !ActivityManager.isProcStateBackground(proc.getCurProcState())) {
                             // Whoever is instigating this is in the foreground, so we will allow it
                             // to go through.
                             return ActivityManager.APP_START_MODE_NORMAL;
@@ -6871,11 +5529,6 @@
         return ptw != null ? ptw.tag : null;
     }
 
-    @VisibleForTesting
-    boolean isActivityStartsLoggingEnabled() {
-        return mConstants.mFlagActivityStartsLoggingEnabled;
-    }
-
     private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
         ProviderInfo pi = null;
         ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
@@ -7011,19 +5664,7 @@
 
     @Override
     public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
-        final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
-        final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
-        outInfo.availMem = getFreeMemory();
-        outInfo.totalMem = getTotalMemory();
-        outInfo.threshold = homeAppMem;
-        outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
-        outInfo.hiddenAppThreshold = cachedAppMem;
-        outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
-                ProcessList.SERVICE_ADJ);
-        outInfo.visibleAppThreshold = mProcessList.getMemLevel(
-                ProcessList.VISIBLE_APP_ADJ);
-        outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
-                ProcessList.FOREGROUND_APP_ADJ);
+        mProcessList.getMemoryInfo(outInfo);
     }
 
     // =========================================================
@@ -7415,7 +6056,7 @@
             }
             cpr.connections.add(conn);
             r.conProviders.add(conn);
-            startAssociationLocked(r.uid, r.processName, r.curProcState,
+            startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
                     cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
             return conn;
         }
@@ -7459,7 +6100,7 @@
         return false;
     }
 
-    private void checkTime(long startTime, String where) {
+    void checkTime(long startTime, String where) {
         long now = SystemClock.uptimeMillis();
         if ((now-startTime) > 50) {
             // If we are taking more than 50ms, log about it.
@@ -7501,6 +6142,7 @@
         ContentProviderRecord cpr;
         ContentProviderConnection conn = null;
         ProviderInfo cpi = null;
+        boolean providerRunning = false;
 
         synchronized(this) {
             long startTime = SystemClock.uptimeMillis();
@@ -7540,8 +6182,6 @@
                 }
             }
 
-            boolean providerRunning = false;
-
             if (cpr != null && cpr.proc != null) {
                 providerRunning = !cpr.proc.killed;
 
@@ -7619,7 +6259,7 @@
                         // back up on the LRU list.  This is good because
                         // content providers are often expensive to start.
                         checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
-                        updateLruProcessLocked(cpr.proc, false, null);
+                        mProcessList.updateLruProcessLocked(cpr.proc, false, null);
                         checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
                     }
                 }
@@ -7866,6 +6506,7 @@
         }
 
         // Wait for the provider to be published...
+        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
         synchronized (cpr) {
             while (cpr.provider == null) {
                 if (cpr.launchingApp == null) {
@@ -7880,13 +6521,22 @@
                     return null;
                 }
                 try {
+                    final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
                     if (DEBUG_MU) Slog.v(TAG_MU,
                             "Waiting to start provider " + cpr
-                            + " launchingApp=" + cpr.launchingApp);
+                            + " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");
                     if (conn != null) {
                         conn.waiting = true;
                     }
-                    cpr.wait();
+                    cpr.wait(wait);
+                    if (cpr.provider == null) {
+                        Slog.wtf(TAG, "Timeout waiting for provider "
+                                + cpi.applicationInfo.packageName + "/"
+                                + cpi.applicationInfo.uid + " for provider "
+                                + name
+                                + " providerRunning=" + providerRunning);
+                        return null;
+                    }
                 } catch (InterruptedException ex) {
                 } finally {
                     if (conn != null) {
@@ -8246,8 +6896,8 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mAppErrors.appNotResponding(host, null, null, false,
-                        "ContentProvider not responding");
+                host.appNotResponding(
+                        null, null, null, null, false, "ContentProvider not responding");
             }
         });
     }
@@ -8255,7 +6905,7 @@
     public final void installSystemProviders() {
         List<ProviderInfo> providers;
         synchronized (this) {
-            ProcessRecord app = mProcessNames.get("system", SYSTEM_UID);
+            ProcessRecord app = mProcessList.mProcessNames.get("system", SYSTEM_UID);
             providers = generateApplicationProvidersLocked(app);
             if (providers != null) {
                 for (int i=providers.size()-1; i>=0; i--) {
@@ -8317,9 +6967,10 @@
         final int matchFlags = GET_PROVIDERS | MATCH_DIRECT_BOOT_UNAWARE;
 
         synchronized (this) {
-            final int NP = mProcessNames.getMap().size();
+            final int NP = mProcessList.mProcessNames.getMap().size();
             for (int ip = 0; ip < NP; ip++) {
-                final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                final SparseArray<ProcessRecord> apps = mProcessList.mProcessNames.getMap().valueAt
+                        (ip);
                 final int NA = apps.size();
                 for (int ia = 0; ia < NA; ia++) {
                     final ProcessRecord app = apps.valueAt(ia);
@@ -8425,67 +7076,6 @@
     // GLOBAL MANAGEMENT
     // =========================================================
 
-    @GuardedBy("this")
-    final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
-            boolean isolated, int isolatedUid) {
-        String proc = customProcess != null ? customProcess : info.processName;
-        final int userId = UserHandle.getUserId(info.uid);
-        int uid = info.uid;
-        if (isolated) {
-            if (isolatedUid == 0) {
-                int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
-                while (true) {
-                    if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
-                            || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
-                        mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
-                    }
-                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
-                    mNextIsolatedProcessUid++;
-                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
-                        // No process for this uid, use it.
-                        break;
-                    }
-                    stepsLeft--;
-                    if (stepsLeft <= 0) {
-                        return null;
-                    }
-                }
-            } else {
-                // Special case for startIsolatedProcess (internal only), where
-                // the uid of the isolated process is specified by the caller.
-                uid = isolatedUid;
-            }
-            getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
-
-            // Register the isolated UID with this application so BatteryStats knows to
-            // attribute resource usage to the application.
-            //
-            // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
-            // about the process state of the isolated UID *before* it is registered with the
-            // owning application.
-            mBatteryStatsService.addIsolatedUid(uid, info.uid);
-            StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
-                    StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
-        }
-        final ProcessRecord r = new ProcessRecord(this, info, proc, uid, getGlobalConfiguration());
-        if (!mBooted && !mBooting
-                && userId == UserHandle.USER_SYSTEM
-                && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
-            // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
-            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
-            r.setPersistent(true);
-            r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
-        }
-        if (isolated && isolatedUid != 0) {
-            // Special case for startIsolatedProcess (internal only) - assume the process
-            // is required by the system server to prevent it being killed.
-            r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
-        }
-        addProcessNameLocked(r);
-        return r;
-    }
-
     private boolean uidOnBackgroundWhitelist(final int uid) {
         final int appId = UserHandle.getAppId(uid);
         final int[] whitelist = mBackgroundAppIdWhitelist;
@@ -8543,12 +7133,13 @@
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             String abiOverride) {
         return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
-                abiOverride);
+                false /* mountExtStorageFull */, abiOverride);
     }
 
+    // TODO: Move to ProcessList?
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
-            boolean disableHiddenApiChecks, String abiOverride) {
+            boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -8558,8 +7149,8 @@
         }
 
         if (app == null) {
-            app = newProcessRecordLocked(info, customProcess, isolated, 0);
-            updateLruProcessLocked(app, false, null);
+            app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0);
+            mProcessList.updateLruProcessLocked(app, false, null);
             updateOomAdjLocked();
         }
 
@@ -8579,9 +7170,9 @@
         }
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
-            startProcessLocked(app, "added application",
+            mProcessList.startProcessLocked(app, "added application",
                     customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
-                    abiOverride);
+                    mountExtStorageFull, abiOverride);
         }
 
         return app;
@@ -8696,51 +7287,12 @@
 
     @Override
     public void notifyLockedProfile(@UserIdInt int userId) {
-        try {
-            if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
-                throw new SecurityException("Only privileged app can call notifyLockedProfile");
-            }
-        } catch (RemoteException ex) {
-            throw new SecurityException("Fail to check is caller a privileged app", ex);
-        }
-
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                if (mUserController.shouldConfirmCredentials(userId)) {
-                    if (mActivityTaskManager.mKeyguardController.isKeyguardLocked()) {
-                        // Showing launcher to avoid user entering credential twice.
-                        final int currentUserId = mUserController.getCurrentUserId();
-                        mAtmInternal.startHomeActivity(currentUserId, "notifyLockedProfile");
-                    }
-                    mStackSupervisor.lockAllProfileTasks(userId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        mAtmInternal.notifyLockedProfile(userId, mUserController.getCurrentUserId());
     }
 
     @Override
     public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
-        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
-        synchronized (this) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
-                        FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
-                        FLAG_ACTIVITY_TASK_ON_HOME);
-                ActivityOptions activityOptions = options != null
-                        ? new ActivityOptions(options)
-                        : ActivityOptions.makeBasic();
-                activityOptions.setLaunchTaskId(
-                        mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
-                mContext.startActivityAsUser(intent, activityOptions.toBundle(),
-                        UserHandle.CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        mAtmInternal.startConfirmDeviceCredentialIntent(intent, options);
     }
 
     @Override
@@ -9064,7 +7616,7 @@
 
         synchronized (this) {
             UidRecord uidRec = mActiveUids.get(uid);
-            return uidRec != null ? uidRec.curProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
+            return uidRec != null ? uidRec.getCurProcState() : PROCESS_STATE_NONEXISTENT;
         }
     }
 
@@ -9250,11 +7802,11 @@
                         Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
                         return;
                     }
-                    if (pr.hasTopUi != hasTopUi) {
+                    if (pr.hasTopUi() != hasTopUi) {
                         if (DEBUG_OOM_ADJ) {
                             Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
                         }
-                        pr.hasTopUi = hasTopUi;
+                        pr.setHasTopUi(hasTopUi);
                         changed = true;
                     }
                 }
@@ -9482,7 +8034,7 @@
         synchronized (this) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                killPackageProcessesLocked(null, appId, userId,
+                mProcessList.killPackageProcessesLocked(null, appId, userId,
                         ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true,
                         reason != null ? reason : "kill uid");
             } finally {
@@ -9626,14 +8178,20 @@
             // If at least 1/3 of our time since the last idle period has been spent
             // with RAM low, then we want to kill processes.
             boolean doKilling = lowRamSinceLastIdle > (timeSinceLastIdle/3);
+            // If the processes' memory has increased by more than 1% of the total memory,
+            // or 10 MB, whichever is greater, then the processes' are eligible to be killed.
+            final long totalMemoryInKb = getTotalMemory() / 1000;
+            final long memoryGrowthThreshold =
+                    Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD);
 
-            for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                ProcessRecord proc = mLruProcesses.get(i);
+            for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+                ProcessRecord proc = mProcessList.mLruProcesses.get(i);
                 if (proc.notCachedSinceIdle) {
                     if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
                             && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
                         if (doKilling && proc.initialIdlePss != 0
-                                && proc.lastPss > ((proc.initialIdlePss*3)/2)) {
+                                && proc.lastPss > ((proc.initialIdlePss * 3) / 2)
+                                && proc.lastPss > (proc.initialIdlePss + memoryGrowthThreshold)) {
                             sb = new StringBuilder(128);
                             sb.append("Kill");
                             sb.append(proc.processName);
@@ -9757,7 +8315,7 @@
                 for (int i=procsToKill.size()-1; i>=0; i--) {
                     ProcessRecord proc = procsToKill.get(i);
                     Slog.i(TAG, "Removing system update proc: " + proc);
-                    removeProcessLocked(proc, true, false, "system update done");
+                    mProcessList.removeProcessLocked(proc, true, false, "system update done");
                 }
             }
 
@@ -9848,7 +8406,7 @@
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
             mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
 
             BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
@@ -9947,16 +8505,17 @@
                         : StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN
         );
 
-        final int relaunchReason = r == null ? ActivityRecord.RELAUNCH_REASON_NONE
+        final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
                         : r.getWindowProcessController().computeRelaunchReason();
-        final String relaunchReasonString = ActivityRecord.relaunchReasonToString(relaunchReason);
+        final String relaunchReasonString = relaunchReasonToString(relaunchReason);
         if (crashInfo.crashTag == null) {
             crashInfo.crashTag = relaunchReasonString;
         } else {
             crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
         }
 
-        addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
+        addErrorToDropBox(
+                eventType, r, processName, null, null, null, null, null, null, crashInfo);
 
         mAppErrors.crashApplication(r, crashInfo);
     }
@@ -10128,7 +8687,7 @@
         StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName,
                 callingPid);
 
-        addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);
+        addErrorToDropBox("wtf", r, processName, null, null, null, tag, null, null, crashInfo);
 
         return r;
     }
@@ -10143,22 +8702,7 @@
         }
 
         synchronized (this) {
-            final int NP = mProcessNames.getMap().size();
-            for (int ip=0; ip<NP; ip++) {
-                SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
-                final int NA = apps.size();
-                for (int ia=0; ia<NA; ia++) {
-                    ProcessRecord p = apps.valueAt(ia);
-                    if (p.thread != null && p.thread.asBinder() == app) {
-                        return p;
-                    }
-                }
-            }
-
-            Slog.w(TAG, "Can't find mystery application for " + reason
-                    + " from pid=" + Binder.getCallingPid()
-                    + " uid=" + Binder.getCallingUid() + ": " + app);
-            return null;
+            return mProcessList.findAppProcessLocked(app, reason);
         }
     }
 
@@ -10227,17 +8771,18 @@
      * Write a description of an error (crash, WTF, ANR) to the drop box.
      * @param eventType to include in the drop box tag ("crash", "wtf", etc.)
      * @param process which caused the error, null means the system server
-     * @param activity which triggered the error, null if unknown
-     * @param parent activity related to the error, null if unknown
+     * @param activityShortComponentName which triggered the error, null if unknown
+     * @param parentShortComponentName activity related to the error, null if unknown
+     * @param parentProcess parent process
      * @param subject line related to the error, null if absent
      * @param report in long form describing the error, null if absent
      * @param dataFile text file to include in the report, null if none
      * @param crashInfo giving an application stack trace, null if absent
      */
     public void addErrorToDropBox(String eventType,
-            ProcessRecord process, String processName, ActivityRecord activity,
-            ActivityRecord parent, String subject,
-            final String report, final File dataFile,
+            ProcessRecord process, String processName, String activityShortComponentName,
+            String parentShortComponentName, ProcessRecord parentProcess,
+            String subject, final String report, final File dataFile,
             final ApplicationErrorReport.CrashInfo crashInfo) {
         // NOTE -- this must never acquire the ActivityManagerService lock,
         // otherwise the watchdog may be prevented from resetting the system.
@@ -10267,14 +8812,16 @@
                     .append(process.isInterestingToUserLocked() ? "Yes" : "No")
                     .append("\n");
         }
-        if (activity != null) {
-            sb.append("Activity: ").append(activity.shortComponentName).append("\n");
+        if (activityShortComponentName != null) {
+            sb.append("Activity: ").append(activityShortComponentName).append("\n");
         }
-        if (parent != null && parent.app != null && parent.app.getPid() != process.pid) {
-            sb.append("Parent-Process: ").append(parent.app.mName).append("\n");
-        }
-        if (parent != null && parent != activity) {
-            sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
+        if (parentShortComponentName != null) {
+            if (parentProcess != null && parentProcess.pid != process.pid) {
+                sb.append("Parent-Process: ").append(parentProcess.processName).append("\n");
+            }
+            if (!parentShortComponentName.equals(activityShortComponentName)) {
+                sb.append("Parent-Activity: ").append(parentShortComponentName).append("\n");
+            }
         }
         if (subject != null) {
             sb.append("Subject: ").append(subject).append("\n");
@@ -10371,8 +8918,8 @@
         synchronized (this) {
 
             // iterate across all processes
-            for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
+            for (int i=mProcessList.mLruProcesses.size()-1; i>=0; i--) {
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!allUsers && app.userId != userId) {
                     continue;
                 }
@@ -10405,44 +8952,6 @@
         return errList;
     }
 
-    static int procStateToImportance(int procState, int memAdj,
-            ActivityManager.RunningAppProcessInfo currApp,
-            int clientTargetSdk) {
-        int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
-                procState, clientTargetSdk);
-        if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
-            currApp.lru = memAdj;
-        } else {
-            currApp.lru = 0;
-        }
-        return imp;
-    }
-
-    @GuardedBy("this")
-    private void fillInProcMemInfoLocked(ProcessRecord app,
-            ActivityManager.RunningAppProcessInfo outInfo,
-            int clientTargetSdk) {
-        outInfo.pid = app.pid;
-        outInfo.uid = app.info.uid;
-        if (mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
-            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
-        }
-        if (app.isPersistent()) {
-            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
-        }
-        if (app.hasActivities()) {
-            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
-        }
-        outInfo.lastTrimLevel = app.trimMemoryLevel;
-        int adj = app.curAdj;
-        int procState = app.curProcState;
-        outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
-        outInfo.importanceReasonCode = app.adjTypeCode;
-        outInfo.processState = app.curProcState;
-        outInfo.isFocused = (app == getTopAppLocked());
-        outInfo.lastActivityTime = app.lastActivityTime;
-    }
-
     @Override
     public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
         enforceNotIsolatedCaller("getRunningAppProcesses");
@@ -10450,8 +8959,6 @@
         final int callingUid = Binder.getCallingUid();
         final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
 
-        // Lazy instantiation of list
-        List<ActivityManager.RunningAppProcessInfo> runList = null;
         final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
                 callingUid) == PackageManager.PERMISSION_GRANTED;
         final int userId = UserHandle.getUserId(callingUid);
@@ -10460,44 +8967,9 @@
 
         synchronized (this) {
             // Iterate across all processes
-            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
-                if ((!allUsers && app.userId != userId)
-                        || (!allUids && app.uid != callingUid)) {
-                    continue;
-                }
-                if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
-                    // Generate process state info for running application
-                    ActivityManager.RunningAppProcessInfo currApp =
-                        new ActivityManager.RunningAppProcessInfo(app.processName,
-                                app.pid, app.getPackageList());
-                    fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
-                    if (app.adjSource instanceof ProcessRecord) {
-                        currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
-                        currApp.importanceReasonImportance =
-                                ActivityManager.RunningAppProcessInfo.procStateToImportance(
-                                        app.adjSourceProcState);
-                    } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
-                        ActivityServiceConnectionsHolder r =
-                                (ActivityServiceConnectionsHolder) app.adjSource;
-                        final int pid = r.getActivityPid();
-                        if (pid != -1) {
-                            currApp.importanceReasonPid = pid;
-                        }
-                    }
-                    if (app.adjTarget instanceof ComponentName) {
-                        currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
-                    }
-                    //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
-                    //        + " lru=" + currApp.lru);
-                    if (runList == null) {
-                        runList = new ArrayList<>();
-                    }
-                    runList.add(currApp);
-                }
-            }
+            return mProcessList.getRunningAppProcessesLocked(allUsers, userId, allUids,
+                    callingUid, clientTargetSdk);
         }
-        return runList;
     }
 
     @Override
@@ -10544,7 +9016,7 @@
                 proc = mPidsSelfLocked.get(Binder.getCallingPid());
             }
             if (proc != null) {
-                fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
+                mProcessList.fillInProcMemInfoLocked(proc, outState, clientTargetSdk);
             }
         }
     }
@@ -10629,24 +9101,26 @@
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
-            if (mActivityTaskManager.getRecentTasks() != null) {
-                mActivityTaskManager.getRecentTasks().dump(pw, dumpAll, dumpPackage);
-            }
+            mAtmInternal.dump(
+                    DUMP_RECENTS_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
-            dumpLastANRLocked(pw);
+            mAtmInternal.dump(
+                    DUMP_LASTANR_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
-            dumpActivityStarterLocked(pw, dumpPackage);
+            mAtmInternal.dump(
+                    DUMP_STARTER_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
             }
-            dumpActivityContainersLocked(pw);
+            mAtmInternal.dump(
+                    DUMP_CONTAINERS_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             // Activities section is dumped as part of the Critical priority dump. Exclude the
             // section if priority is Normal.
             if (!dumpNormalPriority) {
@@ -10654,7 +9128,8 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                mAtmInternal.dump(
+                        DUMP_ACTIVITIES_CMD, fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
             }
             if (mAssociations.size() > 0) {
                 pw.println();
@@ -10743,9 +9218,7 @@
 
             if ("activities".equals(cmd) || "a".equals(cmd)) {
                 // output proto is ActivityManagerServiceDumpActivitiesProto
-                synchronized (this) {
-                    writeActivitiesToProtoLocked(proto);
-                }
+                mAtmInternal.writeActivitiesToProto(proto);
             } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
                 // output proto is ActivityManagerServiceDumpBroadcastsProto
                 synchronized (this) {
@@ -10784,7 +9257,7 @@
                 // default option, dump everything, output is ActivityManagerServiceProto
                 synchronized (this) {
                     long activityToken = proto.start(ActivityManagerServiceProto.ACTIVITIES);
-                    writeActivitiesToProtoLocked(proto);
+                    mAtmInternal.writeActivitiesToProto(proto);
                     proto.end(activityToken);
 
                     long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
@@ -10811,32 +9284,12 @@
         if (opti < args.length) {
             String cmd = args[opti];
             opti++;
-            if ("activities".equals(cmd) || "a".equals(cmd)) {
-                synchronized (this) {
-                    dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
-                }
-            } else if ("lastanr".equals(cmd)) {
-                synchronized (this) {
-                    dumpLastANRLocked(pw);
-                }
-            } else if ("lastanr-traces".equals(cmd)) {
-                synchronized (this) {
-                    dumpLastANRTracesLocked(pw);
-                }
-            } else if ("starter".equals(cmd)) {
-                synchronized (this) {
-                    dumpActivityStarterLocked(pw, dumpPackage);
-                }
-            } else if ("containers".equals(cmd)) {
-                synchronized (this) {
-                    dumpActivityContainersLocked(pw);
-                }
-            } else if ("recents".equals(cmd) || "r".equals(cmd)) {
-                synchronized (this) {
-                    if (mActivityTaskManager.getRecentTasks() != null) {
-                        mActivityTaskManager.getRecentTasks().dump(pw, true /* dumpAll */, dumpPackage);
-                    }
-                }
+            if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)
+                    || DUMP_LASTANR_CMD.equals(cmd) || DUMP_LASTANR_TRACES_CMD.equals(cmd)
+                    || DUMP_STARTER_CMD.equals(cmd) || DUMP_CONTAINERS_CMD.equals(cmd)
+                    || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+                mAtmInternal.dump(
+                        cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage);
             } else if ("binder-proxies".equals(cmd)) {
                 if (opti >= args.length) {
                     dumpBinderProxies(pw);
@@ -10972,8 +9425,8 @@
                 LockGuard.dump(fd, pw, args);
             } else {
                 // Dumping a single activity?
-                if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacksOnly,
-                        dumpFocusedStackOnly)) {
+                if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
+                        dumpVisibleStacksOnly, dumpFocusedStackOnly)) {
                     ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
                     int res = shell.exec(this, null, fd, null, args, null,
                             new ResultReceiver(null));
@@ -11010,96 +9463,6 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
-        // The output proto of "activity --proto activities" is ActivityManagerServiceDumpActivitiesProto
-        mStackSupervisor.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
-    }
-
-    private void dumpLastANRLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
-        if (mLastANRState == null) {
-            pw.println("  <no ANR has occurred since boot>");
-        } else {
-            pw.println(mLastANRState);
-        }
-    }
-
-    private void dumpLastANRTracesLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
-
-        final File[] files = new File(ANR_TRACE_DIR).listFiles();
-        if (ArrayUtils.isEmpty(files)) {
-            pw.println("  <no ANR has occurred since boot>");
-            return;
-        }
-        // Find the latest file.
-        File latest = null;
-        for (File f : files) {
-            if ((latest == null) || (latest.lastModified() < f.lastModified())) {
-                latest = f;
-            }
-        }
-        pw.print("File: ");
-        pw.print(latest.getName());
-        pw.println();
-        try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
-            String line;
-            while ((line = in.readLine()) != null) {
-                pw.println(line);
-            }
-        } catch (IOException e) {
-            pw.print("Unable to read: ");
-            pw.print(e);
-            pw.println();
-        }
-    }
-
-    private void dumpActivityContainersLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
-        mStackSupervisor.dumpChildrenNames(pw, " ");
-        pw.println(" ");
-    }
-
-    private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
-        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
-        mActivityTaskManager.getActivityStartController().dump(pw, "", dumpPackage);
-    }
-
-    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
-        dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
-                "ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
-    }
-
-    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
-        pw.println(header);
-
-        boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
-                dumpPackage);
-        boolean needSep = printedAnything;
-
-        boolean printed = ActivityStackSupervisor.printThisActivity(pw,
-                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
-                "  ResumedActivity: ");
-        if (printed) {
-            printedAnything = true;
-            needSep = false;
-        }
-
-        if (dumpPackage == null) {
-            if (needSep) {
-                pw.println();
-            }
-            printedAnything = true;
-            mStackSupervisor.dump(pw, "  ");
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
-    }
-
     void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
         pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)");
@@ -11202,7 +9565,7 @@
         return -1;
     }
 
-    boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, SparseArray<UidRecord> uids,
+    boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, ActiveUids uids,
                 String header, boolean needSep) {
         boolean printed = false;
         for (int i=0; i<uids.size(); i++) {
@@ -11270,6 +9633,7 @@
                 "Counts of Binder Proxies held by SYSTEM");
     }
 
+    // TODO: Move to ProcessList?
     @GuardedBy("this")
     void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
@@ -11279,9 +9643,9 @@
         pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)");
 
         if (dumpAll) {
-            final int NP = mProcessNames.getMap().size();
+            final int NP = mProcessList.mProcessNames.getMap().size();
             for (int ip=0; ip<NP; ip++) {
-                SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+                SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
                 final int NA = procs.size();
                 for (int ia=0; ia<NA; ia++) {
                     ProcessRecord r = procs.valueAt(ia);
@@ -11303,10 +9667,10 @@
             }
         }
 
-        if (mIsolatedProcesses.size() > 0) {
+        if (mProcessList.mIsolatedProcesses.size() > 0) {
             boolean printed = false;
-            for (int i=0; i<mIsolatedProcesses.size(); i++) {
-                ProcessRecord r = mIsolatedProcesses.valueAt(i);
+            for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+                ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
                 if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                     continue;
                 }
@@ -11359,17 +9723,13 @@
             }
         }
 
-        if (mLruProcesses.size() > 0) {
+        if (mProcessList.getLruSizeLocked() > 0) {
             if (needSep) {
                 pw.println();
             }
-            pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
-                    pw.print(" total, non-act at ");
-                    pw.print(mLruProcesses.size()-mLruProcessActivityStart);
-                    pw.print(", non-svc at ");
-                    pw.print(mLruProcesses.size()-mLruProcessServiceStart);
-                    pw.println("):");
-            dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", false, dumpPackage);
+            mProcessList.dumpLruListHeaderLocked(pw);
+            dumpProcessOomList(pw, this, mProcessList.mLruProcesses, "    ", "Proc", "PERS", false,
+                    dumpPackage);
             needSep = true;
         }
 
@@ -11423,11 +9783,11 @@
                     "Starting Norm", "Restarting PERS", dumpPackage);
         }
 
-        if (mRemovedProcesses.size() > 0) {
+        if (mProcessList.mRemovedProcesses.size() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  Processes that are being removed:");
-            dumpProcessList(pw, this, mRemovedProcesses, "    ",
+            dumpProcessList(pw, this, mProcessList.mRemovedProcesses, "    ",
                     "Removed Norm", "Removed PERS", dumpPackage);
         }
 
@@ -11448,71 +9808,20 @@
             needSep = false;
             mUserController.dump(pw, dumpAll);
         }
-        if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
-                || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
-            if (needSep) {
-                pw.println();
-                needSep = false;
-            }
-            pw.println("  mHomeProcess: " + mActivityTaskManager.mHomeProcess);
-        }
-        if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
-                || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
-            if (needSep) {
-                pw.println();
-                needSep = false;
-            }
-            pw.println("  mPreviousProcess: " + mActivityTaskManager.mPreviousProcess);
-        }
-        if (dumpAll && (mActivityTaskManager.mPreviousProcess == null || dumpPackage == null
-                || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
-            StringBuilder sb = new StringBuilder(128);
-            sb.append("  mPreviousProcessVisibleTime: ");
-            TimeUtils.formatDuration(mActivityTaskManager.mPreviousProcessVisibleTime, sb);
-            pw.println(sb);
-        }
-        if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
-                || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
-            if (needSep) {
-                pw.println();
-                needSep = false;
-            }
-            pw.println("  mHeavyWeightProcess: " + mActivityTaskManager.mHeavyWeightProcess);
-        }
-        if (dumpAll && mPendingStarts.size() > 0) {
+
+        needSep = mAtmInternal.dumpForProcesses(fd, pw, dumpAll, dumpPackage, dumpAppId, needSep,
+                mTestPssMode, mWakefulness);
+
+        if (dumpAll && mProcessList.mPendingStarts.size() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  mPendingStarts: ");
-            for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
-                pw.println("    " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
+            for (int i = 0, len = mProcessList.mPendingStarts.size(); i < len; ++i ) {
+                pw.println("    " + mProcessList.mPendingStarts.keyAt(i) + ": "
+                        + mProcessList.mPendingStarts.valueAt(i));
             }
         }
-        if (dumpPackage == null) {
-            pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
-            mStackSupervisor.dumpDisplayConfigs(pw, "  ");
-        }
         if (dumpAll) {
-            if (dumpPackage == null) {
-                pw.println("  mConfigWillChange: "
-                        + mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
-            }
-            if (mActivityTaskManager.mCompatModePackages.getPackages().size() > 0) {
-                boolean printed = false;
-                for (Map.Entry<String, Integer> entry
-                        : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
-                    String pkg = entry.getKey();
-                    int mode = entry.getValue();
-                    if (dumpPackage != null && !dumpPackage.equals(pkg)) {
-                        continue;
-                    }
-                    if (!printed) {
-                        pw.println("  mScreenCompatPackages:");
-                        printed = true;
-                    }
-                    pw.print("    "); pw.print(pkg); pw.print(": ");
-                            pw.print(mode); pw.println();
-                }
-            }
             final int NI = mUidObservers.getRegisteredCallbackCount();
             boolean printed = false;
             for (int i=0; i<NI; i++) {
@@ -11569,11 +9878,6 @@
                 }
             }
         }
-        if (dumpPackage == null) {
-            pw.println("  mWakefulness="
-                    + PowerManagerInternal.wakefulnessToString(mWakefulness));
-            mActivityTaskManager.dumpSleepStates(pw, mTestPssMode);
-        }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
             if (dumpPackage == null || dumpPackage.equals(mDebugApp)
@@ -11587,9 +9891,6 @@
                         + " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
             }
         }
-        if (mActivityTaskManager.mCurAppTimeTracker != null) {
-            mActivityTaskManager.mCurAppTimeTracker.dumpWithHeader(pw, "  ", true);
-        }
         if (mMemWatchProcesses.getMap().size() > 0) {
             pw.println("  Mem watch processes:");
             final ArrayMap<String, SparseArray<Pair<Long, String>>> procs
@@ -11654,40 +9955,10 @@
                 pw.println("  mNativeDebuggingApp=" + mNativeDebuggingApp);
             }
         }
-        if (mActivityTaskManager.mAllowAppSwitchUids.size() > 0) {
-            boolean printed = false;
-            for (int i = 0; i < mActivityTaskManager.mAllowAppSwitchUids.size(); i++) {
-                ArrayMap<String, Integer> types = mActivityTaskManager.mAllowAppSwitchUids.valueAt(i);
-                for (int j = 0; j < types.size(); j++) {
-                    if (dumpPackage == null ||
-                            UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
-                        if (needSep) {
-                            pw.println();
-                            needSep = false;
-                        }
-                        if (!printed) {
-                            pw.println("  mAllowAppSwitchUids:");
-                            printed = true;
-                        }
-                        pw.print("    User ");
-                        pw.print(mActivityTaskManager.mAllowAppSwitchUids.keyAt(i));
-                        pw.print(": Type ");
-                        pw.print(types.keyAt(j));
-                        pw.print(" = ");
-                        UserHandle.formatUid(pw, types.valueAt(j).intValue());
-                        pw.println();
-                    }
-                }
-            }
-        }
         if (dumpPackage == null) {
             if (mAlwaysFinishActivities) {
                 pw.println("  mAlwaysFinishActivities=" + mAlwaysFinishActivities);
             }
-            if (mActivityTaskManager.mController != null) {
-                pw.println("  mController=" + mActivityTaskManager.mController
-                        + " mControllerIsAMonkey=" + mActivityTaskManager.mControllerIsAMonkey);
-            }
             if (dumpAll) {
                 pw.println("  Total persistent processes: " + numPers);
                 pw.println("  mProcessesReady=" + mProcessesReady
@@ -11700,11 +9971,9 @@
                 pw.print("  mLastPowerCheckUptime=");
                         TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
                         pw.println("");
-                pw.println("  mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
-                pw.println("  mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
-                pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
+                pw.println("  mAdjSeq=" + mAdjSeq + " mLruSeq=" + mProcessList.mLruSeq);
                 pw.println("  mNumNonCachedProcs=" + mNumNonCachedProcs
-                        + " (" + mLruProcesses.size() + " total)"
+                        + " (" + mProcessList.getLruSizeLocked() + " total)"
                         + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
                         + " mNumServiceProcs=" + mNumServiceProcs
                         + " mNewNumServiceProcs=" + mNewNumServiceProcs);
@@ -11750,9 +10019,9 @@
     void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
         int numPers = 0;
 
-        final int NP = mProcessNames.getMap().size();
+        final int NP = mProcessList.mProcessNames.getMap().size();
         for (int ip=0; ip<NP; ip++) {
-            SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip);
+            SparseArray<ProcessRecord> procs = mProcessList.mProcessNames.getMap().valueAt(ip);
             final int NA = procs.size();
             for (int ia = 0; ia<NA; ia++) {
                 ProcessRecord r = procs.valueAt(ia);
@@ -11766,8 +10035,8 @@
             }
         }
 
-        for (int i=0; i<mIsolatedProcesses.size(); i++) {
-            ProcessRecord r = mIsolatedProcesses.valueAt(i);
+        for (int i=0; i<mProcessList.mIsolatedProcesses.size(); i++) {
+            ProcessRecord r = mProcessList.mIsolatedProcesses.valueAt(i);
             if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                 continue;
             }
@@ -11800,14 +10069,17 @@
             uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS);
         }
 
-        if (mLruProcesses.size() > 0) {
+        if (mProcessList.getLruSizeLocked() > 0) {
             long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
-            int total = mLruProcesses.size();
+            int total = mProcessList.getLruSizeLocked();
             proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
-            writeProcessOomListToProto(proto, ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
-                    mLruProcesses,false, dumpPackage);
+            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT,
+                    total - mProcessList.mLruProcessActivityStart);
+            proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT,
+                    total - mProcessList.mLruProcessServiceStart);
+            writeProcessOomListToProto(proto,
+                    ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
+                    mProcessList.mLruProcesses,false, dumpPackage);
             proto.end(lruToken);
         }
 
@@ -11818,7 +10090,8 @@
                     if (!r.pkgList.containsKey(dumpPackage)) {
                         continue;
                     }
-                    r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
+                    r.writeToProto(proto,
+                            ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED);
                 }
             }
         }
@@ -11832,7 +10105,8 @@
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
                     }
-                    it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
+                    it.writeToProto(proto,
+                            ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS);
                 }
             }
         }
@@ -11842,11 +10116,12 @@
             if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
                 continue;
             }
-            r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
+            r.writeToProto(proto,
+                    ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS);
         }
 
-        for (int i=0; i<mRemovedProcesses.size(); i++) {
-            ProcessRecord r = mRemovedProcesses.get(i);
+        for (int i = 0; i < mProcessList.mRemovedProcesses.size(); i++) {
+            ProcessRecord r = mProcessList.mRemovedProcesses.get(i);
             if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
                 continue;
             }
@@ -11863,39 +10138,10 @@
 
         writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage);
         mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage);
+        mAtmInternal.writeProcessesToProto(proto, dumpPackage);
 
         if (dumpPackage == null) {
             mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER);
-            getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION);
-            proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, mActivityTaskManager.getTopDisplayFocusedStack().mConfigWillChange);
-        }
-
-        if (mActivityTaskManager.mHomeProcess != null && (dumpPackage == null
-                || mActivityTaskManager.mHomeProcess.mPkgList.contains(dumpPackage))) {
-            ((ProcessRecord) mActivityTaskManager.mHomeProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC);
-        }
-
-        if (mActivityTaskManager.mPreviousProcess != null && (dumpPackage == null
-                || mActivityTaskManager.mPreviousProcess.mPkgList.contains(dumpPackage))) {
-            ((ProcessRecord) mActivityTaskManager.mPreviousProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC);
-            proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mActivityTaskManager.mPreviousProcessVisibleTime);
-        }
-
-        if (mActivityTaskManager.mHeavyWeightProcess != null && (dumpPackage == null
-                || mActivityTaskManager.mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
-            ((ProcessRecord) mActivityTaskManager.mHeavyWeightProcess.mOwner).writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC);
-        }
-
-        for (Map.Entry<String, Integer> entry
-                : mActivityTaskManager.mCompatModePackages.getPackages().entrySet()) {
-            String pkg = entry.getKey();
-            int mode = entry.getValue();
-            if (dumpPackage == null || dumpPackage.equals(pkg)) {
-                long compatToken = proto.start(ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES);
-                proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
-                proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE, mode);
-                proto.end(compatToken);
-            }
         }
 
         final int NI = mUidObservers.getRegisteredCallbackCount();
@@ -11928,8 +10174,6 @@
                     PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
             proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
             proto.end(sleepToken);
-
-            mActivityTaskManager.writeSleepStateToProto(proto);
         }
 
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -11945,11 +10189,6 @@
             }
         }
 
-        if (mActivityTaskManager.mCurAppTimeTracker != null) {
-            mActivityTaskManager.mCurAppTimeTracker.writeToProto(
-                    proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
-        }
-
         if (mMemWatchProcesses.getMap().size() > 0) {
             final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
             ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
@@ -12006,12 +10245,6 @@
 
         if (dumpPackage == null) {
             proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
-            if (mActivityTaskManager.mController != null) {
-                final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
-                proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mActivityTaskManager.mController.toString());
-                proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mActivityTaskManager.mControllerIsAMonkey);
-                proto.end(token);
-            }
             proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
             proto.write(ActivityManagerServiceDumpProcessesProto.PROCESSES_READY, mProcessesReady);
             proto.write(ActivityManagerServiceDumpProcessesProto.SYSTEM_READY, mSystemReady);
@@ -12021,10 +10254,8 @@
             proto.write(ActivityManagerServiceDumpProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
             proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
             proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
-            mStackSupervisor.mGoingToSleep.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP);
-            mStackSupervisor.mLaunchingActivity.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY);
             proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
-            proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mLruSeq);
+            proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mProcessList.mLruSeq);
             proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
             proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
             proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
@@ -12105,7 +10336,7 @@
             int opti, boolean dumpAll) {
         boolean needSep = false;
 
-        if (mLruProcesses.size() > 0) {
+        if (mProcessList.getLruSizeLocked() > 0) {
             if (needSep) pw.println();
             needSep = true;
             pw.println("  OOM levels:");
@@ -12125,13 +10356,16 @@
             printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
 
             if (needSep) pw.println();
-            pw.print("  Process OOM control ("); pw.print(mLruProcesses.size());
+            pw.print("  Process OOM control ("); pw.print(mProcessList.getLruSizeLocked());
                     pw.print(" total, non-act at ");
-                    pw.print(mLruProcesses.size()-mLruProcessActivityStart);
+                    pw.print(mProcessList.getLruSizeLocked()
+                            - mProcessList.mLruProcessActivityStart);
                     pw.print(", non-svc at ");
-                    pw.print(mLruProcesses.size()-mLruProcessServiceStart);
+                    pw.print(mProcessList.getLruSizeLocked()
+                            - mProcessList.mLruProcessServiceStart);
                     pw.println("):");
-            dumpProcessOomList(pw, this, mLruProcesses, "    ", "Proc", "PERS", true, null);
+            dumpProcessOomList(pw, this, mProcessList.mLruProcesses, "    ", "Proc", "PERS", true,
+                    null);
             needSep = true;
         }
 
@@ -12249,95 +10483,6 @@
         }
     }
 
-    /**
-     * There are three things that cmd can be:
-     *  - a flattened component name that matches an existing activity
-     *  - the cmd arg isn't the flattened component name of an existing activity:
-     *    dump all activity whose component contains the cmd as a substring
-     *  - A hex number of the ActivityRecord object instance.
-     *
-     *  @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
-     *  @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
-     */
-    protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
-            int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
-        ArrayList<ActivityRecord> activities;
-
-        synchronized (this) {
-            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
-                    dumpFocusedStackOnly);
-        }
-
-        if (activities.size() <= 0) {
-            return false;
-        }
-
-        String[] newArgs = new String[args.length - opti];
-        System.arraycopy(args, opti, newArgs, 0, args.length - opti);
-
-        TaskRecord lastTask = null;
-        boolean needSep = false;
-        for (int i=activities.size()-1; i>=0; i--) {
-            ActivityRecord r = activities.get(i);
-            if (needSep) {
-                pw.println();
-            }
-            needSep = true;
-            synchronized (this) {
-                final TaskRecord task = r.getTask();
-                if (lastTask != task) {
-                    lastTask = task;
-                    pw.print("TASK "); pw.print(lastTask.affinity);
-                            pw.print(" id="); pw.print(lastTask.taskId);
-                            pw.print(" userId="); pw.println(lastTask.userId);
-                    if (dumpAll) {
-                        lastTask.dump(pw, "  ");
-                    }
-                }
-            }
-            dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll);
-        }
-        return true;
-    }
-
-    /**
-     * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
-     * there is a thread associated with the activity.
-     */
-    private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
-            final ActivityRecord r, String[] args, boolean dumpAll) {
-        String innerPrefix = prefix + "  ";
-        synchronized (this) {
-            pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
-                    pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
-                    pw.print(" pid=");
-                    if (r.hasProcess()) pw.println(r.app.getPid());
-                    else pw.println("(not running)");
-            if (dumpAll) {
-                r.dump(pw, innerPrefix);
-            }
-        }
-        if (r.attachedToProcess()) {
-            // flush anything that is already in the PrintWriter since the thread is going
-            // to write to the file descriptor directly
-            pw.flush();
-            try {
-                TransferPipe tp = new TransferPipe();
-                try {
-                    r.app.getThread().dumpActivity(tp.getWriteFd(),
-                            r.appToken, innerPrefix, args);
-                    tp.go(fd);
-                } finally {
-                    tp.kill();
-                }
-            } catch (IOException e) {
-                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
-            } catch (RemoteException e) {
-                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
-            }
-        }
-    }
-
     void writeBroadcastsToProtoLocked(ProtoOutputStream proto) {
         if (mRegisteredReceivers.size() > 0) {
             Iterator it = mRegisteredReceivers.values().iterator();
@@ -12662,12 +10807,13 @@
             if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
                 proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
             }
-            if (r.foregroundActivities) {
+            if (r.hasForegroundActivities()) {
                 proto.write(ProcessOomProto.ACTIVITIES, true);
             } else if (r.hasForegroundServices()) {
                 proto.write(ProcessOomProto.SERVICES, true);
             }
-            proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
+            proto.write(ProcessOomProto.STATE,
+                    ProcessList.makeProcStateProtoEnum(r.getCurProcState()));
             proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.trimMemoryLevel);
             r.writeToProto(proto, ProcessOomProto.PROC);
             proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
@@ -12688,12 +10834,12 @@
             if (inclDetails) {
                 long detailToken = proto.start(ProcessOomProto.DETAIL);
                 proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
-                proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.curRawAdj);
+                proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.getCurRawAdj());
                 proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
                 proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
                 proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
                 proto.write(ProcessOomProto.Detail.CURRENT_STATE,
-                        ProcessList.makeProcStateProtoEnum(r.curProcState));
+                        ProcessList.makeProcStateProtoEnum(r.getCurProcState()));
                 proto.write(ProcessOomProto.Detail.SET_STATE,
                         ProcessList.makeProcStateProtoEnum(r.setProcState));
                 proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
@@ -12759,14 +10905,14 @@
                     break;
             }
             char foreground;
-            if (r.foregroundActivities) {
+            if (r.hasForegroundActivities()) {
                 foreground = 'A';
             } else if (r.hasForegroundServices()) {
                 foreground = 'S';
             } else {
                 foreground = ' ';
             }
-            String procState = ProcessList.makeProcStateString(r.curProcState);
+            String procState = ProcessList.makeProcStateString(r.getCurProcState());
             pw.print(prefix);
             pw.print(r.isPersistent() ? persistentLabel : normalLabel);
             pw.print(" #");
@@ -12814,13 +10960,14 @@
                 pw.print(prefix);
                 pw.print("    ");
                 pw.print("oom: max="); pw.print(r.maxAdj);
-                pw.print(" curRaw="); pw.print(r.curRawAdj);
+                pw.print(" curRaw="); pw.print(r.getCurRawAdj());
                 pw.print(" setRaw="); pw.print(r.setRawAdj);
                 pw.print(" cur="); pw.print(r.curAdj);
                 pw.print(" set="); pw.println(r.setAdj);
                 pw.print(prefix);
                 pw.print("    ");
-                pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
+                pw.print("state: cur="); pw.print(
+                        ProcessList.makeProcStateString(r.getCurProcState()));
                 pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
                 pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
                 pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
@@ -12853,35 +11000,9 @@
 
     ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs,
             String[] args) {
-        ArrayList<ProcessRecord> procs;
         synchronized (this) {
-            if (args != null && args.length > start
-                    && args[start].charAt(0) != '-') {
-                procs = new ArrayList<ProcessRecord>();
-                int pid = -1;
-                try {
-                    pid = Integer.parseInt(args[start]);
-                } catch (NumberFormatException e) {
-                }
-                for (int i=mLruProcesses.size()-1; i>=0; i--) {
-                    ProcessRecord proc = mLruProcesses.get(i);
-                    if (proc.pid > 0 && proc.pid == pid) {
-                        procs.add(proc);
-                    } else if (allPkgs && proc.pkgList != null
-                            && proc.pkgList.containsKey(args[start])) {
-                        procs.add(proc);
-                    } else if (proc.processName.equals(args[start])) {
-                        procs.add(proc);
-                    }
-                }
-                if (procs.size() <= 0) {
-                    return null;
-                }
-            } else {
-                procs = new ArrayList<ProcessRecord>(mLruProcesses);
-            }
+            return mProcessList.collectProcessesLocked(start, allPkgs, args);
         }
-        return procs;
     }
 
     final void dumpGraphicsHardwareUsage(FileDescriptor fd,
@@ -14475,13 +12596,13 @@
             mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
                     false, null).dumpLocked();
             catPw.println();
-            dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
+            mAtmInternal.dump(DUMP_ACTIVITIES_CMD, null, catPw, emptyArgs, 0, false, false, null);
             catPw.flush();
         }
         dropBuilder.append(catSw.toString());
         StatsLog.write(StatsLog.LOW_MEM_REPORTED);
         addErrorToDropBox("lowmem", null, "system_server", null,
-                null, tag.toString(), dropBuilder.toString(), null, null);
+                null, null, tag.toString(), dropBuilder.toString(), null, null);
         //Slog.i(TAG, "Sent to dropbox:");
         //Slog.i(TAG, dropBuilder.toString());
         synchronized (ActivityManagerService.this) {
@@ -14576,7 +12697,7 @@
      * app that was passed in must remain on the process lists.
      */
     @GuardedBy("this")
-    private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
+    final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
             boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
         if (index >= 0) {
             removeLruProcessLocked(app);
@@ -14610,11 +12731,11 @@
         app.waitingToKill = null;
         app.forcingToImportant = null;
         updateProcessForegroundLocked(app, false, false);
-        app.foregroundActivities = false;
+        app.setHasForegroundActivities(false);
         app.hasShownUi = false;
         app.treatLikeActivity = false;
         app.hasAboveClient = false;
-        app.hasClientActivities = false;
+        app.setHasClientActivities(false);
 
         mServices.killServicesLocked(app, allowRestart);
 
@@ -14716,7 +12837,7 @@
             if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                     "Removing non-persistent process during cleanup: " + app);
             if (!replacingPid) {
-                removeProcessNameLocked(app.processName, app.uid, app);
+                mProcessList.removeProcessNameLocked(app.processName, app.uid, app);
             }
             mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
         } else if (!app.removed) {
@@ -14740,9 +12861,9 @@
             if (index < 0) {
                 ProcessList.remove(app.pid);
             }
-            addProcessNameLocked(app);
+            mProcessList.addProcessNameLocked(app);
             app.pendingStart = false;
-            startProcessLocked(app, "restart", app.processName);
+            mProcessList.startProcessLocked(app, "restart", app.processName);
             return true;
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
@@ -15479,15 +13600,7 @@
     }
 
     private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
-        for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-            ProcessRecord r = mLruProcesses.get(i);
-            if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
-                try {
-                    r.thread.dispatchPackageBroadcast(cmd, packages);
-                } catch (RemoteException ex) {
-                }
-            }
-        }
+        mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
     }
 
     private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
@@ -15896,8 +14009,10 @@
                                     }
                                 } else {
                                     if (killProcess) {
-                                        killPackageProcessesLocked(ssp, UserHandle.getAppId(
-                                                intent.getIntExtra(Intent.EXTRA_UID, -1)),
+                                        final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
+                                                -1);
+                                        mProcessList.killPackageProcessesLocked(ssp,
+                                                UserHandle.getAppId(extraUid),
                                                 userId, ProcessList.INVALID_ADJ,
                                                 false, true, true, false, "change " + ssp);
                                     }
@@ -15938,7 +14053,7 @@
                                     + " ssp=" + ssp + " data=" + data);
                             return ActivityManager.BROADCAST_SUCCESS;
                         }
-                        mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+                        mAtmInternal.onPackageReplaced(aInfo);
                         mServices.updateServiceApplicationInfoLocked(aInfo);
                         sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
                                 new String[] {ssp}, userId);
@@ -16583,11 +14698,13 @@
             activeInstr.mResultClass = className;
 
             boolean disableHiddenApiChecks = ai.usesNonSdkApi()
-                    || (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+                    || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
             if (disableHiddenApiChecks) {
                 enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
                         "disable hidden API checks");
             }
+            final boolean mountExtStorageFull = isCallerShell()
+                    && (flags & INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL) != 0;
 
             final long origId = Binder.clearCallingIdentity();
             // Instrumentation can kill and relaunch even persistent processes
@@ -16600,7 +14717,7 @@
             }
 
             ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
-                    abiOverride);
+                    mountExtStorageFull, abiOverride);
             app.setActiveInstrumentation(activeInstr);
             activeInstr.mFinished = false;
             activeInstr.mRunningProcesses.add(app);
@@ -16613,6 +14730,11 @@
         return true;
     }
 
+    private boolean isCallerShell() {
+        final int callingUid = Binder.getCallingUid();
+        return callingUid == SHELL_UID || callingUid == ROOT_UID;
+    }
+
     /**
      * Report errors that occur while attempting to start Instrumentation.  Always writes the
      * error to the logs, but if somebody is watching, send the report there too.  This enables
@@ -16886,7 +15008,7 @@
         }
     }
 
-    private void noteUidProcessState(final int uid, final int state) {
+    void noteUidProcessState(final int uid, final int state) {
         mBatteryStatsService.noteUidProcessState(uid, state);
         mAppOpsService.updateUidProcState(uid, state);
         if (mTrackingAssociations) {
@@ -16918,6 +15040,7 @@
     private final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
             new ComputeOomAdjWindowCallback();
 
+    /** These methods are called inline during computeOomAdjLocked(), on the same thread */
     private final class ComputeOomAdjWindowCallback
             implements WindowProcessController.ComputeOomAdjCallback {
 
@@ -17056,8 +15179,9 @@
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
             app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_BACKGROUND);
-            app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
-            app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ;
+            app.setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+            app.curAdj = ProcessList.CACHED_APP_MAX_ADJ;
+            app.setCurRawAdj(ProcessList.CACHED_APP_MAX_ADJ);
             app.completedAdjSeq = app.adjSeq;
             return false;
         }
@@ -17073,7 +15197,7 @@
         final int logUid = mCurOomAdjUid;
 
         int prevAppAdj = app.curAdj;
-        int prevProcState = app.curProcState;
+        int prevProcState = app.getCurProcState();
 
         if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
             // The max adjustment doesn't allow this app to be anything
@@ -17083,10 +15207,10 @@
             }
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
-            app.curRawAdj = app.maxAdj;
-            app.foregroundActivities = false;
+            app.setCurRawAdj(app.maxAdj);
+            app.setHasForegroundActivities(false);
             app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
-            app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
+            app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
             // System processes can do UI, and when they do we want to have
             // them trim their memory after the user leaves the UI.  To
             // facilitate this, here we need to determine whether or not it
@@ -17096,7 +15220,7 @@
                 app.systemNoUi = false;
                 app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
                 app.adjType = "pers-top-activity";
-            } else if (app.hasTopUi) {
+            } else if (app.hasTopUi()) {
                 // sched group/proc state adjustment is below
                 app.systemNoUi = false;
                 app.adjType = "pers-top-ui";
@@ -17106,11 +15230,11 @@
             if (!app.systemNoUi) {
               if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) {
                   // screen on, promote UI
-                  app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+                  app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
                   app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
               } else {
                   // screen off, restrict UI scheduling
-                  app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+                  app.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
                   app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_RESTRICTED);
               }
             }
@@ -17246,7 +15370,7 @@
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app);
                 }
-            } else if (app.hasOverlayUi) {
+            } else if (app.hasOverlayUi()) {
                 // The process is display an overlay UI.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                 procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
@@ -17360,7 +15484,7 @@
         // there are applications dependent on our services or providers, but
         // this gives us a baseline and makes sure we don't get into an
         // infinite recursion.
-        app.curRawAdj = adj;
+        app.setCurRawAdj(adj);
         app.hasStartedServices = false;
         app.adjSeq = mAdjSeq;
 
@@ -17475,8 +15599,8 @@
                                 continue;
                             }
                         }
-                        int clientAdj = client.curRawAdj;
-                        int clientProcState = client.curProcState;
+                        int clientAdj = client.getCurRawAdj();
+                        int clientProcState = client.getCurProcState();
                         if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                             // If the other app is cached for any reason, for purposes here
                             // we are going to consider it empty.  The specific cached state
@@ -17637,8 +15761,8 @@
                             }
                         }
                         if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                                && (cr.flags&Context.BIND_SHOWING_UI) != 0) {
-                            app.pendingUiClean = true;
+                                && (cr.flags & Context.BIND_SHOWING_UI) != 0) {
+                            app.setPendingUiClean(true);
                         }
                         if (adjType != null) {
                             app.adjType = adjType;
@@ -17717,8 +15841,8 @@
                         continue;
                     }
                 }
-                int clientAdj = client.curRawAdj;
-                int clientProcState = client.curProcState;
+                int clientAdj = client.getCurRawAdj();
+                int clientProcState = client.getCurProcState();
                 if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
                     // If the other app is cached for any reason, for purposes here
                     // we are going to consider it empty.
@@ -17879,7 +16003,7 @@
         }
 
         if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            if (app.hasClientActivities) {
+            if (app.hasClientActivities()) {
                 // This is a cached process, but with client activities.  Mark it so.
                 procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
                 app.adjType = "cch-client-act";
@@ -17919,7 +16043,7 @@
             }
         }
 
-        app.curRawAdj = adj;
+        app.setCurRawAdj(adj);
 
         //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
         //      " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj);
@@ -17946,12 +16070,12 @@
         // keep it out of the cached vaues.
         app.curAdj = app.modifyRawOomAdj(adj);
         app.setCurrentSchedulingGroup(schedGroup);
-        app.curProcState = procState;
-        app.foregroundActivities = foregroundActivities;
+        app.setCurProcState(procState);
+        app.setHasForegroundActivities(foregroundActivities);
         app.completedAdjSeq = mAdjSeq;
 
         // if curAdj or curProcState improved, then this process was promoted
-        return app.curAdj < prevAppAdj || app.curProcState < prevProcState;
+        return app.curAdj < prevAppAdj || app.getCurProcState() < prevProcState;
     }
 
     /**
@@ -18098,12 +16222,11 @@
         for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
             ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);;
         }
-        mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
+        mPendingPssProcesses.ensureCapacity(mProcessList.getLruSizeLocked());
         mPendingPssProcesses.clear();
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
-            if (app.thread == null
-                    || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+        for (int i = mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
+            if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
                 continue;
             }
             if (memLowered || (always && now >
@@ -18112,7 +16235,7 @@
                 app.pssProcState = app.setProcState;
                 app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
                         : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
-                app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+                app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
                         app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
                 mPendingPssProcesses.add(app);
             }
@@ -18162,8 +16285,7 @@
                 processingBroadcasts = true;
             }
         }
-        return !processingBroadcasts
-                && (mAtmInternal.isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
+        return !processingBroadcasts && mAtmInternal.canGcNow();
     }
 
     /**
@@ -18178,7 +16300,7 @@
         if (canGcNowLocked()) {
             while (mProcessesToGc.size() > 0) {
                 ProcessRecord proc = mProcessesToGc.remove(0);
-                if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
+                if (proc.getCurRawAdj() > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
                     if ((proc.lastRequestedGc+mConstants.GC_MIN_INTERVAL)
                             <= SystemClock.uptimeMillis()) {
                         // To avoid spamming the system, we will GC processes one
@@ -18278,10 +16400,10 @@
         final long curUptime = SystemClock.uptimeMillis();
         final long uptimeSince = curUptime - mLastPowerCheckUptime;
         mLastPowerCheckUptime = curUptime;
-        int i = mLruProcesses.size();
+        int i = mProcessList.mLruProcesses.size();
         while (i > 0) {
             i--;
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
                 if (app.lastCpuTime <= 0) {
                     continue;
@@ -18305,7 +16427,7 @@
                 if (doCpuKills && uptimeSince > 0) {
                     // What is the limit for this process?
                     int cpuLimit;
-                    long checkDur = curUptime - app.whenUnimportant;
+                    long checkDur = curUptime - app.getWhenUnimportant();
                     if (checkDur <= mConstants.POWER_CHECK_INTERVAL) {
                         cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1;
                     } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL*2)
@@ -18343,8 +16465,8 @@
             long nowElapsed) {
         boolean success = true;
 
-        if (app.curRawAdj != app.setRawAdj) {
-            app.setRawAdj = app.curRawAdj;
+        if (app.getCurRawAdj() != app.setRawAdj) {
+            app.setRawAdj = app.getCurRawAdj();
         }
 
         int changes = 0;
@@ -18467,12 +16589,12 @@
                 }
             }
         }
-        if (app.repForegroundActivities != app.foregroundActivities) {
-            app.repForegroundActivities = app.foregroundActivities;
+        if (app.repForegroundActivities != app.hasForegroundActivities()) {
+            app.repForegroundActivities = app.hasForegroundActivities();
             changes |= ProcessChangeItem.CHANGE_ACTIVITIES;
         }
-        if (app.getReportedProcState() != app.curProcState) {
-            app.setReportedProcState(app.curProcState);
+        if (app.getReportedProcState() != app.getCurProcState()) {
+            app.setReportedProcState(app.getCurProcState());
             if (app.thread != null) {
                 try {
                     if (false) {
@@ -18485,8 +16607,8 @@
                 }
             }
         }
-        if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
-                || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
+        if (app.setProcState == PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(app.getCurProcState(), app.setProcState)) {
             if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
                 // Experimental code to more aggressively collect pss while
                 // running test...  the problem is that this tends to collect
@@ -18496,46 +16618,45 @@
                 long startTime = SystemClock.currentThreadTimeMillis();
                 long pss = Debug.getPss(app.pid, mTmpLong, null);
                 long endTime = SystemClock.currentThreadTimeMillis();
-                recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1],
+                recordPssSampleLocked(app, app.getCurProcState(), pss, mTmpLong[0], mTmpLong[1],
                         mTmpLong[2], ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now);
                 mPendingPssProcesses.remove(app);
                 Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
-                        + " to " + app.curProcState + ": "
+                        + " to " + app.getCurProcState() + ": "
                         + (SystemClock.uptimeMillis()-start) + "ms");
             }
             app.lastStateTime = now;
-            app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+            app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
                     app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
             if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
                     + ProcessList.makeProcStateString(app.setProcState) + " to "
-                    + ProcessList.makeProcStateString(app.curProcState) + " next pss in "
+                    + ProcessList.makeProcStateString(app.getCurProcState()) + " next pss in "
                     + (app.nextPssTime-now) + ": " + app);
         } else {
             if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL)
                     && now > (app.lastStateTime+ProcessList.minTimeFromStateChange(
                     mTestPssMode)))) {
                 if (requestPssLocked(app, app.setProcState)) {
-                    app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState,
+                    app.nextPssTime = ProcessList.computeNextPssTime(app.getCurProcState(),
                             app.procStateMemTracker, mTestPssMode, mAtmInternal.isSleeping(), now);
                 }
             } else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
                     "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now));
         }
-        if (app.setProcState != app.curProcState) {
+        if (app.setProcState != app.getCurProcState()) {
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
                 String msg = "Proc state change of " + app.processName
-                        + " to " + ProcessList.makeProcStateString(app.curProcState)
-                        + " (" + app.curProcState + ")" + ": " + app.adjType;
+                        + " to " + ProcessList.makeProcStateString(app.getCurProcState())
+                        + " (" + app.getCurProcState() + ")" + ": " + app.adjType;
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
             }
             boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
-            boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE;
+            boolean curImportant = app.getCurProcState() < ActivityManager.PROCESS_STATE_SERVICE;
             if (setImportant && !curImportant) {
-                // This app is no longer something we consider important enough to allow to
-                // use arbitrary amounts of battery power.  Note
-                // its current CPU time to later know to kill it if
-                // it is not behaving well.
-                app.whenUnimportant = now;
+                // This app is no longer something we consider important enough to allow to use
+                // arbitrary amounts of battery power. Note its current CPU time to later know to
+                // kill it if it is not behaving well.
+                app.setWhenUnimportant(now);
                 app.lastCpuTime = 0;
             }
             // Inform UsageStats of important process state change
@@ -18544,7 +16665,7 @@
 
             maybeUpdateLastTopTime(app, now);
 
-            app.setProcState = app.curProcState;
+            app.setProcState = app.getCurProcState();
             if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
                 app.notCachedSinceIdle = false;
             }
@@ -18553,7 +16674,7 @@
             } else {
                 app.procStateChanged = true;
             }
-        } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime)
+        } else if (app.reportedInteraction && (nowElapsed - app.getInteractionEventTime())
                 > mConstants.USAGE_STATS_INTERACTION_INTERVAL) {
             // For apps that sit around for a long time in the interactive state, we need
             // to report this at least once a day so they don't go idle.
@@ -18675,8 +16796,7 @@
             }
         }
         pendingChange.change = change;
-        pendingChange.processState = uidRec != null
-                ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
+        pendingChange.processState = uidRec != null ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT;
         pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
         pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
         if (uidRec != null) {
@@ -18707,7 +16827,7 @@
     private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
             String authority) {
         if (app == null) return;
-        if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+        if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
             UserState userState = mUserController.getStartedUserState(app.userId);
             if (userState == null) return;
             final long now = SystemClock.elapsedRealtime();
@@ -18727,7 +16847,7 @@
         if (DEBUG_USAGE_STATS) {
             Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
                     + "] state changes: old = " + app.setProcState + ", new = "
-                    + app.curProcState);
+                    + app.getCurProcState());
         }
         if (mUsageStatsService == null) {
             return;
@@ -18736,24 +16856,26 @@
         // To avoid some abuse patterns, we are going to be careful about what we consider
         // to be an app interaction.  Being the top activity doesn't count while the display
         // is sleeping, nor do short foreground services.
-        if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP) {
+        if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP) {
             isInteraction = true;
-            app.fgInteractionTime = 0;
-        } else if (app.curProcState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
-            if (app.fgInteractionTime == 0) {
-                app.fgInteractionTime = nowElapsed;
+            app.setFgInteractionTime(0);
+        } else if (app.getCurProcState() <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+            if (app.getFgInteractionTime() == 0) {
+                app.setFgInteractionTime(nowElapsed);
                 isInteraction = false;
             } else {
-                isInteraction = nowElapsed > app.fgInteractionTime
+                isInteraction = nowElapsed > app.getFgInteractionTime()
                         + mConstants.SERVICE_USAGE_INTERACTION_TIME;
             }
         } else {
-            isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-            app.fgInteractionTime = 0;
+            isInteraction =
+                    app.getCurProcState() <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+            app.setFgInteractionTime(0);
         }
-        if (isInteraction && (!app.reportedInteraction || (nowElapsed-app.interactionEventTime)
+        if (isInteraction
+                && (!app.reportedInteraction || (nowElapsed - app.getInteractionEventTime())
                 > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) {
-            app.interactionEventTime = nowElapsed;
+            app.setInteractionEventTime(nowElapsed);
             String[] packages = app.getPackageList();
             if (packages != null) {
                 for (int i = 0; i < packages.length; i++) {
@@ -18764,13 +16886,13 @@
         }
         app.reportedInteraction = isInteraction;
         if (!isInteraction) {
-            app.interactionEventTime = 0;
+            app.setInteractionEventTime(0);
         }
     }
 
     private void maybeUpdateLastTopTime(ProcessRecord app, long nowUptime) {
         if (app.setProcState <= ActivityManager.PROCESS_STATE_TOP
-                && app.curProcState > ActivityManager.PROCESS_STATE_TOP) {
+                && app.getCurProcState() > ActivityManager.PROCESS_STATE_TOP) {
             app.lastTopTime = nowUptime;
         }
     }
@@ -18830,13 +16952,14 @@
 
     // TODO(b/111541062): This method is only used for updating OOM adjustments. We need to update
     // the logic there and in mBatteryStatsService to make them aware of multiple resumed activities
-    private ActivityRecord resumedAppLocked() {
-        final ActivityRecord act = mStackSupervisor.getTopResumedActivity();
+    ProcessRecord getTopAppLocked() {
+        final WindowProcessController wpc = mAtmInternal != null ? mAtmInternal.getTopApp() : null;
+        final ProcessRecord r = wpc != null ? (ProcessRecord) wpc.mOwner : null;
         String pkg;
         int uid;
-        if (act != null) {
-            pkg = act.packageName;
-            uid = act.info.applicationInfo.uid;
+        if (r != null) {
+            pkg = r.processName;
+            uid = r.info.uid;
         } else {
             pkg = null;
             uid = -1;
@@ -18862,7 +16985,7 @@
             }
 
         }
-        return act;
+        return r;
     }
 
     /**
@@ -18874,9 +16997,7 @@
      */
     @GuardedBy("this")
     final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
-        final ActivityRecord TOP_ACT = resumedAppLocked();
-        final ProcessRecord TOP_APP = TOP_ACT != null && TOP_ACT.hasProcess()
-                ? (ProcessRecord) TOP_ACT.app.mOwner : null;
+        final ProcessRecord TOP_APP = getTopAppLocked();
         final boolean wasCached = app.cached;
 
         mAdjSeq++;
@@ -18885,12 +17006,12 @@
         // If our app is currently cached, we know it, and that is it.  Otherwise,
         // we don't know it yet, and it needs to now be cached we will then
         // need to do a complete oom adj.
-        final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ
-                ? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
+        final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ
+                ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ;
         boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
                 SystemClock.uptimeMillis());
         if (oomAdjAll
-                && (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ)) {
+                && (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) {
             // Changed to/from cached state, so apps after it in the LRU
             // list may also be changed.
             updateOomAdjLocked();
@@ -18899,23 +17020,13 @@
     }
 
     @GuardedBy("this")
-    ProcessRecord getTopAppLocked() {
-        final ActivityRecord TOP_ACT = resumedAppLocked();
-        if (TOP_ACT != null && TOP_ACT.hasProcess()) {
-            return (ProcessRecord) TOP_ACT.app.mOwner;
-        } else {
-            return null;
-        }
-    }
-
-    @GuardedBy("this")
     final void updateOomAdjLocked() {
         mOomAdjProfiler.oomAdjStarted();
         final ProcessRecord TOP_APP = getTopAppLocked();
         final long now = SystemClock.uptimeMillis();
         final long nowElapsed = SystemClock.elapsedRealtime();
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
-        final int N = mLruProcesses.size();
+        final int N = mProcessList.getLruSizeLocked();
 
         // Reset state in all uid records.
         for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -18925,8 +17036,8 @@
             uidRec.reset();
         }
 
-        if (mStackSupervisor != null) {
-            mStackSupervisor.rankTaskLayersIfNeeded();
+        if (mAtmInternal != null) {
+            mAtmInternal.rankTaskLayersIfNeeded();
         }
 
         mAdjSeq++;
@@ -18976,11 +17087,11 @@
 
         // need to reset cycle state before calling computeOomAdjLocked because of service connections
         for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             app.containsCycle = false;
         }
         for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 app.procStateChanged = false;
                 computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
@@ -18991,14 +17102,14 @@
                 // If we haven't yet assigned the final cached adj
                 // to the process, do that now.
                 if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
-                    switch (app.curProcState) {
+                    switch (app.getCurProcState()) {
                         case PROCESS_STATE_CACHED_ACTIVITY:
                         case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         case ActivityManager.PROCESS_STATE_CACHED_RECENT:
                             // This process is a cached process holding activities...
                             // assign it the next cached value for that type, and then
                             // step that cached level.
-                            app.curRawAdj = curCachedAdj;
+                            app.setCurRawAdj(curCachedAdj);
                             app.curAdj = app.modifyRawOomAdj(curCachedAdj);
                             if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
                                     + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
@@ -19021,7 +17132,7 @@
                             // long-running services that have dropped down to the
                             // cached level will be treated as empty (since their process
                             // state is still as a service), which is what we want.
-                            app.curRawAdj = curEmptyAdj;
+                            app.setCurRawAdj(curEmptyAdj);
                             app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
                             if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
                                     + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
@@ -19055,7 +17166,7 @@
             retryCycles = false;
 
             for (int i=0; i<N; i++) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
                     app.adjSeq--;
                     app.completedAdjSeq--;
@@ -19063,7 +17174,7 @@
             }
 
             for (int i=0; i<N; i++) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
 
                     if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
@@ -19074,12 +17185,12 @@
         }
 
         for (int i=N-1; i>=0; i--) {
-            ProcessRecord app = mLruProcesses.get(i);
+            ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!app.killedByAm && app.thread != null) {
                 applyOomAdjLocked(app, true, now, nowElapsed);
 
                 // Count the number of process types.
-                switch (app.curProcState) {
+                switch (app.getCurProcState()) {
                     case PROCESS_STATE_CACHED_ACTIVITY:
                     case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         mNumCachedHiddenProcs++;
@@ -19120,8 +17231,8 @@
                     final UidRecord uidRec = app.uidRecord;
                     if (uidRec != null) {
                         uidRec.ephemeral = app.info.isInstantApp();
-                        if (uidRec.curProcState > app.curProcState) {
-                            uidRec.curProcState = app.curProcState;
+                        if (uidRec.getCurProcState() > app.getCurProcState()) {
+                            uidRec.setCurProcState(app.getCurProcState());
                         }
                         if (app.hasForegroundServices()) {
                             uidRec.foregroundServices = true;
@@ -19129,7 +17240,7 @@
                     }
                 }
 
-                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
+                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
                         && !app.killedByAm) {
                     numTrimming++;
                 }
@@ -19167,9 +17278,9 @@
         // has gone down since last time.
         if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
                 + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
-                + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);
+                + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
         if (memFactor > mLastMemoryLevel) {
-            if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {
+            if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
                 memFactor = mLastMemoryLevel;
                 if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
             }
@@ -19179,7 +17290,7 @@
             StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
         }
         mLastMemoryLevel = memFactor;
-        mLastNumProcesses = mLruProcesses.size();
+        mLastNumProcesses = mProcessList.getLruSizeLocked();
         boolean allChanged = mProcessStats.setMemFactorLocked(
                 memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
         final int trackerMemFactor = mProcessStats.getMemFactorLocked();
@@ -19207,12 +17318,12 @@
             if (factor < minFactor) factor = minFactor;
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
             for (int i=N-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
                     setProcessTrackerStateLocked(app, trackerMemFactor, now);
                     app.procStateChanged = false;
                 }
-                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
+                if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
                         && !app.killedByAm) {
                     if (app.trimMemoryLevel < curLevel && app.thread != null) {
                         try {
@@ -19235,7 +17346,7 @@
                                 break;
                         }
                     }
-                } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+                } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
                         && !app.killedByAm) {
                     if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
                             && app.thread != null) {
@@ -19250,8 +17361,8 @@
                     }
                     app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                 } else {
-                    if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                            || app.systemNoUi) && app.pendingUiClean) {
+                    if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                            || app.systemNoUi) && app.hasPendingUiClean()) {
                         // If this application is now in the background and it
                         // had done UI, then give it the special trim level to
                         // have it free UI resources.
@@ -19265,7 +17376,7 @@
                             } catch (RemoteException e) {
                             }
                         }
-                        app.pendingUiClean = false;
+                        app.setPendingUiClean(false);
                     }
                     if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
                         try {
@@ -19285,13 +17396,13 @@
                 mLowRamStartTime = 0;
             }
             for (int i=N-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
+                ProcessRecord app = mProcessList.mLruProcesses.get(i);
                 if (allChanged || app.procStateChanged) {
                     setProcessTrackerStateLocked(app, trackerMemFactor, now);
                     app.procStateChanged = false;
                 }
-                if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
-                        || app.systemNoUi) && app.pendingUiClean) {
+                if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                        || app.systemNoUi) && app.hasPendingUiClean()) {
                     if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
                             && app.thread != null) {
                         try {
@@ -19303,7 +17414,7 @@
                         } catch (RemoteException e) {
                         }
                     }
-                    app.pendingUiClean = false;
+                    app.setPendingUiClean(false);
                 }
                 app.trimMemoryLevel = 0;
             }
@@ -19312,7 +17423,7 @@
         if (mAlwaysFinishActivities) {
             // Need to do this on its own message because the stack may not
             // be in a consistent state at this point.
-            mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
+            mAtmInternal.scheduleDestroyAllActivities("always-finish");
         }
 
         if (allChanged) {
@@ -19328,14 +17439,14 @@
         for (int i=mActiveUids.size()-1; i>=0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
             int uidChange = UidRecord.CHANGE_PROCSTATE;
-            if (uidRec.curProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
-                    && (uidRec.setProcState != uidRec.curProcState
+            if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
+                    && (uidRec.setProcState != uidRec.getCurProcState()
                            || uidRec.setWhitelist != uidRec.curWhitelist)) {
-                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
-                        "Changes in " + uidRec + ": proc state from " + uidRec.setProcState
-                        + " to " + uidRec.curProcState + ", whitelist from " + uidRec.setWhitelist
+                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
+                        + ": proc state from " + uidRec.setProcState + " to "
+                        + uidRec.getCurProcState() + ", whitelist from " + uidRec.setWhitelist
                         + " to " + uidRec.curWhitelist);
-                if (ActivityManager.isProcStateBackground(uidRec.curProcState)
+                if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
                         && !uidRec.curWhitelist) {
                     // UID is now in the background (and not on the temp whitelist).  Was it
                     // previously in the foreground (or on the temp whitelist)?
@@ -19368,17 +17479,16 @@
                 }
                 final boolean wasCached = uidRec.setProcState
                         > ActivityManager.PROCESS_STATE_RECEIVER;
-                final boolean isCached = uidRec.curProcState
+                final boolean isCached = uidRec.getCurProcState()
                         > ActivityManager.PROCESS_STATE_RECEIVER;
-                if (wasCached != isCached ||
-                        uidRec.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                if (wasCached != isCached || uidRec.setProcState == PROCESS_STATE_NONEXISTENT) {
                     uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
                 }
-                uidRec.setProcState = uidRec.curProcState;
+                uidRec.setProcState = uidRec.getCurProcState();
                 uidRec.setWhitelist = uidRec.curWhitelist;
                 uidRec.setIdle = uidRec.idle;
                 enqueueUidChangeLocked(uidRec, -1, uidChange);
-                noteUidProcessState(uidRec.uid, uidRec.curProcState);
+                noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
                 if (uidRec.foregroundServices) {
                     mServices.foregroundServiceProcStateChangedLocked(uidRec);
                 }
@@ -19539,7 +17649,7 @@
                 continue;
             }
             // If process state is not changed, then there's nothing to do.
-            if (uidRec.setProcState == uidRec.curProcState) {
+            if (uidRec.setProcState == uidRec.getCurProcState()) {
                 continue;
             }
             final int blockState = getBlockStateForUid(uidRec);
@@ -19549,7 +17659,7 @@
                 continue;
             }
             synchronized (uidRec.networkStateLock) {
-                uidRec.curProcStateSeq = ++mProcStateSeqCounter;
+                uidRec.curProcStateSeq = ++mProcessList.mProcStateSeqCounter; // TODO: use method
                 if (blockState == NETWORK_STATE_BLOCK) {
                     if (blockingUids == null) {
                         blockingUids = new ArrayList<>();
@@ -19572,8 +17682,8 @@
             return;
         }
 
-        for (int i = mLruProcesses.size() - 1; i >= 0; --i) {
-            final ProcessRecord app = mLruProcesses.get(i);
+        for (int i = mProcessList.mLruProcesses.size() - 1; i >= 0; --i) {
+            final ProcessRecord app = mProcessList.mLruProcesses.get(i);
             if (!blockingUids.contains(app.uid)) {
                 continue;
             }
@@ -19603,8 +17713,9 @@
     @VisibleForTesting
     int getBlockStateForUid(UidRecord uidRec) {
         // Denotes whether uid's process state is currently allowed network access.
-        final boolean isAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.curProcState)
-                || isProcStateAllowedWhileOnRestrictBackground(uidRec.curProcState);
+        final boolean isAllowed =
+                isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState())
+                || isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState());
         // Denotes whether uid's process state was previously allowed network access.
         final boolean wasAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState)
                 || isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState);
@@ -19771,8 +17882,8 @@
     final void trimApplicationsLocked() {
         // First remove any unused application processes whose package
         // has been removed.
-        for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
-            final ProcessRecord app = mRemovedProcesses.get(i);
+        for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = mProcessList.mRemovedProcesses.get(i);
             if (!app.hasActivitiesOrRecentTasks()
                     && app.curReceivers.isEmpty() && app.services.size() == 0) {
                 Slog.i(
@@ -19790,7 +17901,7 @@
                     }
                 }
                 cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
-                mRemovedProcesses.remove(i);
+                mProcessList.mRemovedProcesses.remove(i);
 
                 if (app.isPersistent()) {
                     addAppLocked(app.info, null, false, null /* ABI override */);
@@ -19816,8 +17927,8 @@
                         + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES);
             }
 
-            for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
-                ProcessRecord r = mLruProcesses.get(i);
+            for (int i = mProcessList.mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+                ProcessRecord r = mProcessList.mLruProcesses.get(i);
                 if (r.thread != null && r.isPersistent()) {
                     sendSignal(r.pid, sig);
                 }
@@ -19944,7 +18055,7 @@
 
         if (proc == null) {
             ArrayMap<String, SparseArray<ProcessRecord>> all
-                    = mProcessNames.getMap();
+                    = mProcessList.mProcessNames.getMap();
             SparseArray<ProcessRecord> procs = all.get(process);
             if (procs != null && procs.size() > 0) {
                 proc = procs.valueAt(0);
@@ -20074,15 +18185,8 @@
     }
 
     void onCoreSettingsChange(Bundle settings) {
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            ProcessRecord processRecord = mLruProcesses.get(i);
-            try {
-                if (processRecord.thread != null) {
-                    processRecord.thread.setCoreSettings(settings);
-                }
-            } catch (RemoteException re) {
-                /* ignore */
-            }
+        synchronized (this) {
+            mProcessList.updateCoreSettingsLocked(settings);
         }
     }
 
@@ -20214,8 +18318,8 @@
                         + android.Manifest.permission.SET_ACTIVITY_WATCHER);
             }
 
-            for (int i = 0; i < mLruProcesses.size(); i++) {
-                ProcessRecord process = mLruProcesses.get(i);
+            for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+                ProcessRecord process = mProcessList.mLruProcesses.get(i);
                 if (!processSanityChecksLocked(process)) {
                     continue;
                 }
@@ -20247,7 +18351,7 @@
 
                 PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
                 pw.println("Binder transaction traces for all processes.\n");
-                for (ProcessRecord process : mLruProcesses) {
+                for (ProcessRecord process : mProcessList.mLruProcesses) {
                     if (!processSanityChecksLocked(process)) {
                         continue;
                     }
@@ -20308,9 +18412,10 @@
         public void killForegroundAppsForUser(int userHandle) {
             synchronized (ActivityManagerService.this) {
                 final ArrayList<ProcessRecord> procs = new ArrayList<>();
-                final int NP = mProcessNames.getMap().size();
+                final int NP = mProcessList.mProcessNames.getMap().size();
                 for (int ip = 0; ip < NP; ip++) {
-                    final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+                    final SparseArray<ProcessRecord> apps =
+                            mProcessList.mProcessNames.getMap().valueAt(ip);
                     final int NA = apps.size();
                     for (int ia = 0; ia < NA; ia++) {
                         final ProcessRecord app = apps.valueAt(ia);
@@ -20320,7 +18425,7 @@
                         }
                         if (app.removed) {
                             procs.add(app);
-                        } else if (app.userId == userHandle && app.foregroundActivities) {
+                        } else if (app.userId == userHandle && app.hasForegroundActivities()) {
                             app.removed = true;
                             procs.add(app);
                         }
@@ -20329,7 +18434,7 @@
 
                 final int N = procs.size();
                 for (int i = 0; i < N; i++) {
-                    removeProcessLocked(procs.get(i), false, true, "kill all fg");
+                    mProcessList.removeProcessLocked(procs.get(i), false, true, "kill all fg");
                 }
             }
         }
@@ -20391,10 +18496,10 @@
                         return;
                     }
                 }
-                if (pr.hasOverlayUi == hasOverlayUi) {
+                if (pr.hasOverlayUi() == hasOverlayUi) {
                     return;
                 }
-                pr.hasOverlayUi = hasOverlayUi;
+                pr.setHasOverlayUi(hasOverlayUi);
                 //Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
                 updateOomAdjLocked(pr, true);
             }
@@ -20450,36 +18555,6 @@
         }
 
         @Override
-        public void saveANRState(String reason) {
-            synchronized (ActivityManagerService.this) {
-                final StringWriter sw = new StringWriter();
-                final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
-                pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
-                if (reason != null) {
-                    pw.println("  Reason: " + reason);
-                }
-                pw.println();
-                mActivityTaskManager.getActivityStartController().dump(pw, "  ", null);
-                pw.println();
-                pw.println("-------------------------------------------------------------------------------");
-                dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
-                        true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
-                        "" /* header */);
-                pw.println();
-                pw.close();
-
-                mLastANRState = sw.toString();
-            }
-        }
-
-        @Override
-        public void clearSavedANRState() {
-            synchronized (ActivityManagerService.this) {
-                mLastANRState = null;
-            }
-        }
-
-        @Override
         public boolean isRuntimeRestarted() {
             return mSystemServiceManager.isRuntimeRestarted();
         }
@@ -20545,6 +18620,28 @@
         }
 
         @Override
+        public List<ProcessMemoryState> getMemoryStateForNativeProcesses() {
+            List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
+            int[] pids = getPidsForCommands(MEMORY_STAT_INTERESTING_NATIVE_PROCESSES);
+            for (int i = 0; i < pids.length; i++) {
+                int pid = pids[i];
+                MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
+                if (memoryStat == null) {
+                    continue;
+                }
+                int uid = getUidForPid(pid);
+                String processName = readCmdlineFromProcfs(pid);
+                int oomScore = -1; // Unused, not included in the NativeProcessMemoryState atom.
+                ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, processName,
+                        oomScore, memoryStat.pgfault, memoryStat.pgmajfault,
+                        memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes,
+                        memoryStat.rssHighWatermarkInBytes);
+                processMemoryStates.add(processMemoryState);
+            }
+            return processMemoryStates;
+        }
+
+        @Override
         public int handleIncomingUser(int callingPid, int callingUid, int userId,
                 boolean allowAll, int allowMode, String name, String callerPackage) {
             return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
@@ -20572,10 +18669,6 @@
             ActivityManagerService.this.trimApplications();
         }
 
-        public void closeSystemDialogs(String reason) {
-            ActivityManagerService.this.closeSystemDialogs(reason);
-        }
-
         public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) {
             synchronized (ActivityManagerService.this) {
                 for (int i = 0; i < procsToKill.size(); i++) {
@@ -20599,8 +18692,8 @@
             if (packageName == null) return false;
 
             synchronized (ActivityManagerService.this) {
-                for (int i = 0; i < mLruProcesses.size(); i++) {
-                    final ProcessRecord pr = mLruProcesses.get(i);
+                for (int i = 0; i < mProcessList.mLruProcesses.size(); i++) {
+                    final ProcessRecord pr = mProcessList.mLruProcesses.get(i);
                     if (pr.uid != uid) {
                         continue;
                     }
@@ -20779,6 +18872,223 @@
                         (ConnectionRecord) cr, null, c));
             }
         }
+
+        public void cleanUpServices(int userId, ComponentName component, Intent baseIntent) {
+            synchronized(ActivityManagerService.this) {
+                mServices.cleanUpServices(userId, component, baseIntent);
+            }
+        }
+
+        public ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+            // Locked intentionally not held as it isn't needed for this case.
+            return ActivityManagerService.this.getActivityInfoForUser(aInfo, userId);
+        }
+
+        public void ensureBootCompleted() {
+            // Locked intentionally not held as it isn't needed for this case.
+            ActivityManagerService.this.ensureBootCompleted();
+        }
+
+        public void updateOomLevelsForDisplay(int displayId) {
+            synchronized(ActivityManagerService.this) {
+                if (mWindowManager != null) {
+                    mProcessList.applyDisplaySize(mWindowManager);
+                }
+            }
+        }
+
+        public boolean isActivityStartsLoggingEnabled() {
+            return mConstants.mFlagActivityStartsLoggingEnabled;
+        }
+
+        public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
+            synchronized(ActivityManagerService.this) {
+                ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
+                        ? UsageEvents.Event.KEYGUARD_SHOWN
+                        : UsageEvents.Event.KEYGUARD_HIDDEN);
+            }
+        }
+
+        @Override
+        public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
+            synchronized (ActivityManagerService.this) {
+                return ActivityManagerService.this.inputDispatchingTimedOut(
+                        pid, aboveSystem, reason);
+            }
+        }
+
+        @Override
+        public boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
+                ApplicationInfo aInfo, String parentShortComponentName, Object parentProc,
+                boolean aboveSystem, String reason) {
+            return ActivityManagerService.this.inputDispatchingTimedOut((ProcessRecord) proc,
+                    activityShortComponentName, aInfo, parentShortComponentName,
+                    (WindowProcessController) parentProc, aboveSystem, reason);
+
+        }
+
+        @Override
+        public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
+            synchronized (ActivityManagerService.this) {
+                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_REPLACE_PENDING
+                        | Intent.FLAG_RECEIVER_FOREGROUND
+                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                        OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                        UserHandle.USER_ALL);
+                if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
+                    intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+                            | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                            | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+                    if (initLocale || !mProcessesReady) {
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    }
+                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                            OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                            UserHandle.USER_ALL);
+                }
+
+                // Send a broadcast to PackageInstallers if the configuration change is interesting
+                // for the purposes of installing additional splits.
+                if (!initLocale && isSplitConfigurationChange(changes)) {
+                    intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                            | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+                    // Typically only app stores will have this permission.
+                    String[] permissions =
+                            new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
+                            permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+                            UserHandle.USER_ALL);
+                }
+            }
+        }
+
+        /**
+         * Returns true if this configuration change is interesting enough to send an
+         * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+         */
+        private boolean isSplitConfigurationChange(int configDiff) {
+            return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+        }
+
+        @Override
+        public void broadcastCloseSystemDialogs(String reason) {
+            synchronized (ActivityManagerService.this) {
+                final Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                        | Intent.FLAG_RECEIVER_FOREGROUND);
+                if (reason != null) {
+                    intent.putExtra("reason", reason);
+                }
+
+                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                        OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL);
+            }
+        }
+
+        @Override
+        public void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.killAllBackgroundProcessesExcept(
+                        minTargetSdk, maxProcState);
+            }
+        }
+
+        @Override
+        public void startProcess(String processName, ApplicationInfo info,
+                boolean knownToBeDead, String hostingType, ComponentName hostingName) {
+            synchronized (ActivityManagerService.this) {
+                startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
+                        hostingType, hostingName, false /* allowWhileBooting */,
+                        false /* isolated */, true /* keepIfLarge */);
+            }
+        }
+
+        @Override
+        public void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
+                ProfilerInfo profilerInfo) {
+            synchronized (ActivityManagerService.this) {
+                if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
+                    setDebugApp(aInfo.processName, true, false);
+                }
+
+                if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
+                    setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+                }
+
+                if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
+                    setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
+                }
+
+                if (profilerInfo != null) {
+                    setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+                }
+            }
+        }
+    }
+
+    long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
+        if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission " + FILTER_EVENTS);
+        }
+        ProcessRecord proc;
+        long timeout;
+        synchronized (this) {
+            synchronized (mPidsSelfLocked) {
+                proc = mPidsSelfLocked.get(pid);
+            }
+            timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
+        }
+
+        if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
+            return -1;
+        }
+
+        return timeout;
+    }
+
+    /**
+     * Handle input dispatching timeouts.
+     * @return whether input dispatching should be aborted or not.
+     */
+    boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
+            ApplicationInfo aInfo, String parentShortComponentName,
+            WindowProcessController parentProcess, boolean aboveSystem, String reason) {
+        if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission " + FILTER_EVENTS);
+        }
+
+        final String annotation;
+        if (reason == null) {
+            annotation = "Input dispatching timed out";
+        } else {
+            annotation = "Input dispatching timed out (" + reason + ")";
+        }
+
+        if (proc != null) {
+            synchronized (this) {
+                if (proc.isDebugging()) {
+                    return false;
+                }
+
+                if (proc.getActiveInstrumentation() != null) {
+                    Bundle info = new Bundle();
+                    info.putString("shortMsg", "keyDispatchingTimedOut");
+                    info.putString("longMsg", annotation);
+                    finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
+                    return true;
+                }
+            }
+            proc.appNotResponding(activityShortComponentName, aInfo,
+                    parentShortComponentName, parentProcess, aboveSystem, annotation);
+        }
+
+        return true;
     }
 
     /**
@@ -20901,8 +19211,8 @@
         }
         try {
             synchronized(this) {
-                killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid), userId,
-                        ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
+                mProcessList.killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid),
+                        userId, ProcessList.FOREGROUND_APP_ADJ, false, true, true, false,
                         "dep: " + packageName);
             }
         } finally {
@@ -20932,33 +19242,9 @@
 
     void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
         final boolean updateFrameworkRes = packagesToUpdate.contains("android");
-        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
-            final ProcessRecord app = mLruProcesses.get(i);
-            if (app.thread == null) {
-                continue;
-            }
 
-            if (userId != UserHandle.USER_ALL && app.userId != userId) {
-                continue;
-            }
+        mProcessList.updateApplicationInfoLocked(packagesToUpdate, userId, updateFrameworkRes);
 
-            final int packageCount = app.pkgList.size();
-            for (int j = 0; j < packageCount; j++) {
-                final String packageName = app.pkgList.keyAt(j);
-                if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
-                    try {
-                        final ApplicationInfo ai = AppGlobals.getPackageManager()
-                                .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
-                        if (ai != null) {
-                            app.thread.scheduleApplicationInfoChanged(ai);
-                        }
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
-                                    packageName, app));
-                    }
-                }
-            }
-        }
         if (updateFrameworkRes) {
             // Update system server components that need to know about changed overlays. Because the
             // overlay is applied in ActivityThread, we need to serialize through its thread too.
@@ -20980,7 +19266,8 @@
     public void attachAgent(String process, String path) {
         try {
             synchronized (this) {
-                ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent");
+                ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM,
+                        "attachAgent");
                 if (proc == null || proc.thread == null) {
                     throw new IllegalArgumentException("Unknown process: " + process);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 40c555f8..96601a2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -17,14 +17,13 @@
 package com.android.server.am;
 
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
-
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -481,12 +480,12 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mTaskInterface.startActivityAndWait(null, null, intent, mimeType,
+                result = mInternal.startActivityAndWait(null, null, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mTaskInterface.startActivityAsUser(null, null, intent, mimeType,
+                res = mInternal.startActivityAsUser(null, null, intent, mimeType,
                         null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
@@ -2902,6 +2901,7 @@
             pw.println("      --receiver-permission <PERMISSION>: Require receiver to hold permission.");
             pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
             pw.println("          [--user <USER_ID> | current] [--no-hidden-api-checks]");
+            pw.println("          [--no-isolated-storage]");
             pw.println("          [--no-window-animation] [--abi <ABI>] <COMPONENT>");
             pw.println("      Start an Instrumentation.  Typically this target <COMPONENT> is in the");
             pw.println("      form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
@@ -2920,6 +2920,8 @@
             pw.println("      --user <USER_ID> | current: Specify user instrumentation runs in;");
             pw.println("          current user if not specified.");
             pw.println("      --no-hidden-api-checks: disable restrictions on use of hidden API.");
+            pw.println("      --no-isolated-storage: don't use isolated storage sandbox and ");
+            pw.println("          mount full external storage");
             pw.println("      --no-window-animation: turn off window animations while running.");
             pw.println("      --abi <ABI>: Launch the instrumented process with the selected ABI.");
             pw.println("          This assumes that the process supports the selected ABI.");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 7080e2b..a0dd878 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -72,9 +72,9 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
@@ -114,7 +114,7 @@
  */
 class ActivityMetricsLogger {
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityMetricsLogger" : TAG_ATM;
 
     // Window modes we are interested in logging. If we ever introduce a new type, we need to add
     // a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array.
@@ -155,7 +155,6 @@
     private final H mHandler;
 
     private ArtManagerInternal mArtManagerInternal;
-    private boolean mDrawingTraceActive;
     private final StringBuilder mStringBuilder = new StringBuilder();
 
     private final class H extends Handler {
@@ -230,14 +229,14 @@
             launchedActivityLaunchToken = launchedActivity.info.launchToken;
             launchedActivityAppRecordRequiredAbi = launchedActivity.app == null
                     ? null
-                    : info.launchedActivity.app.getRequiredAbi();
+                    : launchedActivity.app.getRequiredAbi();
             reason = info.reason;
             startingWindowDelayMs = info.startingWindowDelayMs;
             bindApplicationDelayMs = info.bindApplicationDelayMs;
             windowsDrawnDelayMs = info.windowsDrawnDelayMs;
             type = getTransitionType(info);
-            processRecord = findProcessForActivity(info.launchedActivity);
-            processName = info.launchedActivity.processName;
+            processRecord = findProcessForActivity(launchedActivity);
+            processName = launchedActivity.processName;
             userId = launchedActivity.userId;
             launchedActivityShortComponentName = launchedActivity.shortComponentName;
             activityRecordIdHashCode = System.identityHashCode(launchedActivity);
@@ -501,7 +500,6 @@
                 if (mWindowingModeTransitionInfo.size() == 0) {
                     reset(true /* abort */, info);
                 }
-                stopFullyDrawnTraceIfNeeded();
             }
         }
     }
@@ -509,14 +507,14 @@
     /**
      * Notifies the tracker that we called immediately before we call bindApplication on the client.
      *
-     * @param app The client into which we'll call bindApplication.
+     * @param appInfo The client into which we'll call bindApplication.
      */
-    void notifyBindApplication(ProcessRecord app) {
+    void notifyBindApplication(ApplicationInfo appInfo) {
         for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
             final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
 
             // App isn't attached to record yet, so match with info.
-            if (info.launchedActivity.appInfo == app.info) {
+            if (info.launchedActivity.appInfo == appInfo) {
                 info.bindApplicationDelayMs = calculateCurrentDelay();
             }
         }
@@ -699,6 +697,13 @@
         if (info == null) {
             return null;
         }
+
+        // Record the handling of the reportFullyDrawn callback in the trace system. This is not
+        // actually used to trace this function, but instead the logical task that this function
+        // fullfils (handling reportFullyDrawn() callbacks).
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "ActivityManager:ReportingFullyDrawn " + info.launchedActivity.packageName);
+
         final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
         builder.setPackageName(r.packageName);
         builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
@@ -720,7 +725,11 @@
                 info.launchedActivity.info.name,
                 info.currentTransitionProcessRunning,
                 startupTimeMs);
-        stopFullyDrawnTraceIfNeeded();
+
+        // Ends the trace started at the beginning of this function. This is located here to allow
+        // the trace slice to have a noticable duration.
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
         BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
@@ -741,7 +750,7 @@
         Log.i(TAG, sb.toString());
     }
 
-    void logActivityStart(Intent intent, ProcessRecord callerApp, ActivityRecord r,
+    void logActivityStart(Intent intent, WindowProcessController callerApp, ActivityRecord r,
             int callingUid, String callingPackage, int callingUidProcState,
             boolean callingUidHasAnyVisibleWindow,
             int realCallingUid, int realCallingUidProcState,
@@ -776,31 +785,31 @@
         builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
         builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
         if (callerApp != null) {
-            builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.processName);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
             builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
-                    processStateAmToProto(callerApp.curProcState));
+                    processStateAmToProto(callerApp.getCurrentProcState()));
             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_CLIENT_ACTIVITIES,
-                    callerApp.hasClientActivities ? 1 : 0);
+                    callerApp.hasClientActivities() ? 1 : 0);
             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_SERVICES,
                     callerApp.hasForegroundServices() ? 1 : 0);
             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_FOREGROUND_ACTIVITIES,
-                    callerApp.foregroundActivities ? 1 : 0);
-            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi ? 1 : 0);
+                    callerApp.hasForegroundActivities() ? 1 : 0);
+            builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_TOP_UI, callerApp.hasTopUi() ? 1 : 0);
             builder.addTaggedData(FIELD_PROCESS_RECORD_HAS_OVERLAY_UI,
-                    callerApp.hasOverlayUi ? 1 : 0);
+                    callerApp.hasOverlayUi() ? 1 : 0);
             builder.addTaggedData(FIELD_PROCESS_RECORD_PENDING_UI_CLEAN,
-                    callerApp.pendingUiClean ? 1 : 0);
-            if (callerApp.interactionEventTime != 0) {
+                    callerApp.hasPendingUiClean() ? 1 : 0);
+            if (callerApp.getInteractionEventTime() != 0) {
                 builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_LAST_INTERACTION_EVENT,
-                        (nowElapsed - callerApp.interactionEventTime));
+                        (nowElapsed - callerApp.getInteractionEventTime()));
             }
-            if (callerApp.fgInteractionTime != 0) {
+            if (callerApp.getFgInteractionTime() != 0) {
                 builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_FG_INTERACTION,
-                        (nowElapsed - callerApp.fgInteractionTime));
+                        (nowElapsed - callerApp.getFgInteractionTime()));
             }
-            if (callerApp.whenUnimportant != 0) {
+            if (callerApp.getWhenUnimportant() != 0) {
                 builder.addTaggedData(FIELD_PROCESS_RECORD_MILLIS_SINCE_UNIMPORTANT,
-                        (nowUptime - callerApp.whenUnimportant));
+                        (nowUptime - callerApp.getWhenUnimportant()));
             }
         }
         builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
@@ -887,10 +896,7 @@
     }
 
     /**
-     * Starts traces for app launch and draw times. We stop the fully drawn trace if its already
-     * active since the app may not have reported fully drawn in the previous launch.
-     *
-     * See {@link android.app.Activity#reportFullyDrawn()}
+     * Starts traces for app launch.
      *
      * @param info
      * */
@@ -898,14 +904,11 @@
         if (info == null) {
             return;
         }
-        stopFullyDrawnTraceIfNeeded();
         int transitionType = getTransitionType(info);
         if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH
                 || transitionType == TYPE_TRANSITION_COLD_LAUNCH) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: "
                     + info.launchedActivity.packageName, 0);
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
-            mDrawingTraceActive = true;
             info.launchTraceActive = true;
         }
     }
@@ -920,11 +923,4 @@
             info.launchTraceActive = false;
         }
     }
-
-    void stopFullyDrawnTraceIfNeeded() {
-        if (mDrawingTraceActive) {
-            Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0);
-            mDrawingTraceActive = false;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6bdceb2..865c774 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -81,22 +81,23 @@
 import static android.os.Build.VERSION_CODES.HONEYCOMB;
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER;
 import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK;
 import static com.android.server.am.ActivityRecordProto.IDENTIFIER;
@@ -114,11 +115,14 @@
 import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG;
 import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG;
 import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY;
 import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY;
 import static com.android.server.am.TaskPersister.DEBUG;
 import static com.android.server.am.TaskPersister.IMAGE_EXTENSION;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -211,7 +215,7 @@
  * An entry in the history stack, representing an activity.
  */
 final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
     private static final String TAG_STATES = TAG + POSTFIX_STATES;
@@ -338,12 +342,6 @@
     int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
     boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
 
-    // This activity is not being relaunched, or being relaunched for a non-resize reason.
-    static final int RELAUNCH_REASON_NONE = 0;
-    // This activity is being relaunched due to windowing mode change.
-    static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
-    // This activity is being relaunched due to a free-resize operation.
-    static final int RELAUNCH_REASON_FREE_RESIZE = 2;
     // Marking the reason why this activity is being relaunched. Mainly used to track that this
     // activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
     // pre-NYC apps that don't have a sense of being resized.
@@ -852,13 +850,12 @@
         }
     }
 
-    ActivityRecord(ActivityTaskManagerService _service, ProcessRecord _caller, int _launchedFromPid,
-            int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
-            ActivityInfo aInfo, Configuration _configuration,
-            ActivityRecord _resultTo, String _resultWho, int _reqCode,
-            boolean _componentSpecified, boolean _rootVoiceInteraction,
-            ActivityStackSupervisor supervisor, ActivityOptions options,
-            ActivityRecord sourceRecord) {
+    ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
+            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
+            String _resolvedType, ActivityInfo aInfo, Configuration _configuration,
+            ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
+            boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
+            ActivityOptions options, ActivityRecord sourceRecord) {
         service = _service;
         appToken = new Token(this, _intent);
         info = aInfo;
@@ -928,8 +925,8 @@
         }
         if ((aInfo.flags & FLAG_MULTIPROCESS) != 0 && _caller != null
                 && (aInfo.applicationInfo.uid == SYSTEM_UID
-                    || aInfo.applicationInfo.uid == _caller.info.uid)) {
-            processName = _caller.processName;
+                    || aInfo.applicationInfo.uid == _caller.mInfo.uid)) {
+            processName = _caller.mName;
         } else {
             processName = aInfo.processName;
         }
@@ -1783,7 +1780,7 @@
             }
             setVisible(true);
             sleeping = false;
-            app.setPendingUiClean(true);
+            app.postPendingUiCleanMsg(true);
             if (reportToClient) {
                 makeClientVisible();
             } else {
@@ -2115,12 +2112,16 @@
             windowFromSameProcessAsActivity =
                     !hasProcess() || app.getPid() == windowPid || windowPid == -1;
         }
+
         if (windowFromSameProcessAsActivity) {
-            return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
+            return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+                    anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
+                    app, false, reason);
         } else {
             // In this case another process added windows using this activity token. So, we call the
             // generic service input dispatch timed out method so that the right process is blamed.
-            return service.inputDispatchingTimedOut(windowPid, false /* aboveSystem */, reason) < 0;
+            return service.mAmInternal.inputDispatchingTimedOut(
+                    windowPid, false /* aboveSystem */, reason) < 0;
         }
     }
 
@@ -2212,12 +2213,13 @@
     }
 
     /**
-     * @return display id to which this record is attached, -1 if not attached.
+     * @return display id to which this record is attached,
+     *         {@link android.view.Display#INVALID_DISPLAY} if not attached.
      */
     int getDisplayId() {
         final ActivityStack stack = getStack();
         if (stack == null) {
-            return -1;
+            return INVALID_DISPLAY;
         }
         return stack.mDisplayId;
     }
@@ -2282,6 +2284,11 @@
             // We don't show starting window for overlay activities.
             return;
         }
+        if (pendingOptions != null
+                && pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+            // Don't show starting window when using shared element transition.
+            return;
+        }
 
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
@@ -3007,17 +3014,6 @@
         mWindowContainerController.registerRemoteAnimations(definition);
     }
 
-    static String relaunchReasonToString(int relaunchReason) {
-        switch (relaunchReason) {
-            case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
-                return "window_resize";
-            case RELAUNCH_REASON_FREE_RESIZE:
-                return "free_resize";
-            default:
-                return null;
-        }
-    }
-
     @Override
     public String toString() {
         if (stringName != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 864bf2d..026c5cc 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -48,41 +48,41 @@
 
 import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
 import static com.android.server.am.ActivityDisplay.POSITION_TOP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TRANSITION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TRANSITION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_FREE_RESIZE;
-import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -103,6 +103,7 @@
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 
+import static com.android.server.am.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
 import static java.lang.Integer.MAX_VALUE;
 
 import android.app.Activity;
@@ -172,7 +173,7 @@
  */
 class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
         implements StackWindowListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
@@ -364,12 +365,12 @@
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
 
-    static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
-    static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
-    static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
-    static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
-    static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
-    static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
+    static final int PAUSE_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+    static final int DESTROY_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 2;
+    static final int LAUNCH_TICK_MSG = FIRST_ACTIVITY_STACK_MSG + 3;
+    static final int STOP_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 4;
+    static final int DESTROY_ACTIVITIES_MSG = FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 6;
 
     private static class ScheduleDestroyArgs {
         final WindowProcessController mOwner;
@@ -1509,8 +1510,6 @@
         prev.getTask().touchActiveTime();
         clearLaunchTime(prev);
 
-        mStackSupervisor.getActivityMetricsLogger().stopFullyDrawnTraceIfNeeded();
-
         mService.updateCpuStats();
 
         if (prev.attachedToProcess()) {
@@ -3878,8 +3877,8 @@
         // The activity that we are finishing may be over the lock screen. In this case, we do not
         // want to consider activities that cannot be shown on the lock screen as running and should
         // proceed with finishing the activity if there is no valid next top running activity.
-        final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */);
+        final ActivityDisplay display = getDisplay();
+        final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
 
         if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
                 && next != null && !next.nowVisible) {
@@ -3903,23 +3902,25 @@
         if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r);
 
         r.setState(FINISHING, "finishCurrentActivityLocked");
-        final boolean finishingActivityInNonFocusedStack
-                = r.getStack() != mStackSupervisor.getTopDisplayFocusedStack()
-                && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
+        final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
+                && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+                        || (next == null && display.topRunningActivity() == null));
 
         if (mode == FINISH_IMMEDIATELY
                 || (prevState == PAUSED
                     && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
-                || finishingActivityInNonFocusedStack
+                || finishingInNonFocusedStackOrNoRunning
                 || prevState == STOPPING
                 || prevState == STOPPED
                 || prevState == ActivityState.INITIALIZING) {
             r.makeFinishingLocked();
             boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
 
-            if (finishingActivityInNonFocusedStack) {
+            if (finishingInNonFocusedStackOrNoRunning) {
                 // Finishing activity that was in paused state and it was in not currently focused
-                // stack, need to make something visible in its place.
+                // stack, need to make something visible in its place. Also if the display does not
+                // have running activity, the configuration may need to be updated for restoring
+                // original orientation of the display.
                 mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8c8146c..257a004 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,8 +21,12 @@
 import static android.Manifest.permission.START_ANY_ACTIVITY;
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
+import static android.app.ActivityManager.START_FLAG_DEBUG;
+import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WaitResult.INVALID_DELAY;
@@ -54,28 +58,6 @@
 import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
-import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
-import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_NONE;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
@@ -91,7 +73,28 @@
 import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
 import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
 import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.am.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -113,6 +116,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
+import android.app.IApplicationThread;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.WaitResult;
@@ -198,7 +202,7 @@
 
 public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
         RecentTasks.Callbacks, RootWindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
     private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -789,8 +793,9 @@
     }
 
     boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            // No restrictions to default display.
+        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+                && displayId == mService.mVr2dDisplayId)) {
+            // No restrictions to default display or vr 2d display.
             return true;
         }
 
@@ -994,8 +999,8 @@
         return candidateTaskId;
     }
 
-    boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
-        final String processName = app.processName;
+    boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
+        final String processName = app.mName;
         boolean didSomething = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
@@ -1009,7 +1014,7 @@
                 final int size = mTmpActivityList.size();
                 for (int i = 0; i < size; i++) {
                     final ActivityRecord activity = mTmpActivityList.get(i);
-                    if (activity.app == null && app.uid == activity.info.applicationInfo.uid
+                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
                             && processName.equals(activity.processName)) {
                         try {
                             if (realStartActivityLocked(activity, app,
@@ -1209,75 +1214,15 @@
     }
 
     ActivityRecord topRunningActivityLocked() {
-        return topRunningActivityLocked(false /* considerKeyguardState */);
-    }
-
-    /**
-     * Returns the top running activity in the focused stack. In the case the focused stack has no
-     * such activity, the next focusable stack on top of a display is returned.
-     * @param considerKeyguardState Indicates whether the locked state should be considered. if
-     *                            {@code true} and the keyguard is locked, only activities that
-     *                            can be shown on top of the keyguard will be considered.
-     * @return The top running activity. {@code null} if none is available.
-     */
-    ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) {
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        ActivityRecord r = focusedStack.topRunningActivityLocked();
-        if (r != null && isValidTopRunningActivity(r, considerKeyguardState)) {
-            return r;
-        }
-
-        // Look in other non-focused and non-home stacks.
         for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-
-            // TODO: We probably want to consider the top fullscreen stack as we could have a pinned
-            // stack on top.
-            final ActivityStack topStack = display.getTopStack();
-
-            // Only consider focusable top stacks other than the current focused one.
-            if (topStack == null || !topStack.isFocusable() || topStack == focusedStack) {
-                continue;
-            }
-
-            final ActivityRecord topActivity = topStack.topRunningActivityLocked();
-
-            // Skip if no top activity.
-            if (topActivity == null) {
-                continue;
-            }
-
-
-            // This activity can be considered the top running activity if we are not
-            // considering the locked state, the keyguard isn't locked, or we can show when
-            // locked.
-            if (isValidTopRunningActivity(topActivity, considerKeyguardState)) {
+            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+            if (topActivity != null) {
                 return topActivity;
             }
         }
-
         return null;
     }
 
-    /**
-     * Verifies an {@link ActivityRecord} can be the top activity based on keyguard state and
-     * whether we are considering it.
-     */
-    private boolean isValidTopRunningActivity(ActivityRecord record,
-            boolean considerKeyguardState) {
-        if (!considerKeyguardState) {
-            return true;
-        }
-
-        final boolean keyguardLocked = getKeyguardController().isKeyguardLocked();
-
-        if (!keyguardLocked) {
-            return true;
-        }
-
-        return record.canShowWhenLocked();
-    }
-
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
             @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
@@ -1299,20 +1244,17 @@
 
             // Don't debug things in the system process
             if (!aInfo.processName.equals("system")) {
-                if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
-                    mService.mAm.setDebugApp(aInfo.processName, true, false);
-                }
-
-                if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
-                    mService.mAm.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
-                }
-
-                if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
-                    mService.mAm.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
-                }
-
-                if (profilerInfo != null) {
-                    mService.mAm.setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo);
+                if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
+                        | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
+                    /**
+                     * Assume safe to call into AMS synchronously because the call that set these
+                     * flags should have originated from AMS which will already have its lock held.
+                     * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String,
+                     * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int)
+                     * TODO(b/80414790): Investigate a better way of untangling this.
+                     */
+                    mService.mAmInternal.setDebugFlagsForStartingActivity(
+                            aInfo, startFlags, profilerInfo);
                 }
             }
             final String intentLaunchToken = intent.getLaunchToken();
@@ -1359,7 +1301,7 @@
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
-    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
+    private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
             boolean andResume, boolean checkConfig) throws RemoteException {
 
         if (!allPausedActivitiesComplete()) {
@@ -1378,7 +1320,6 @@
         beginDeferResume();
 
         try {
-            final WindowProcessController proc = app.getWindowProcessController();
             r.startFreezingScreenLocked(proc, 0);
 
             // schedule launch ticks to collect information about slow apps.
@@ -1414,15 +1355,15 @@
 
             final int applicationInfoUid =
                     (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
-            if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
+            if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
                 Slog.wtf(TAG,
                         "User ID for activity changing for " + r
                                 + " appInfo.uid=" + r.appInfo.uid
                                 + " info.ai.uid=" + applicationInfoUid
-                                + " old=" + r.app + " new=" + app);
+                                + " old=" + r.app + " new=" + proc);
             }
 
-            app.waitingToKill = null;
+            proc.clearWaitingToKill();
             r.launchCount++;
             r.lastLaunchTime = SystemClock.uptimeMillis();
 
@@ -1441,7 +1382,7 @@
             }
 
             try {
-                if (app.thread == null) {
+                if (!proc.hasThread()) {
                     throw new RemoteException();
                 }
                 List<ResultInfo> results = null;
@@ -1467,50 +1408,29 @@
                 r.forceNewConfig = false;
                 mService.getAppWarningsLocked().onStartActivity(r);
                 r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
-                ProfilerInfo profilerInfo = null;
-                if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
-                    if (mService.mAm.mProfileProc == null || mService.mAm.mProfileProc == app) {
-                        mService.mAm.mProfileProc = app;
-                        ProfilerInfo profilerInfoSvc = mService.mAm.mProfilerInfo;
-                        if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
-                            if (profilerInfoSvc.profileFd != null) {
-                                try {
-                                    profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
-                                } catch (IOException e) {
-                                    profilerInfoSvc.closeFd();
-                                }
-                            }
+                ProfilerInfo profilerInfo = proc.onStartActivity(mService.mTopProcessState);
 
-                            profilerInfo = new ProfilerInfo(profilerInfoSvc);
-                        }
-                    }
-                }
-
-                app.hasShownUi = true;
-                app.pendingUiClean = true;
-                app.forceProcessStateUpTo(mService.mTopProcessState);
                 // Because we could be starting an Activity in the system process this may not go
                 // across a Binder interface which would create a new Configuration. Consequently
                 // we have to always create a new Configuration here.
 
                 final MergedConfiguration mergedConfiguration = new MergedConfiguration(
-                        app.getWindowProcessController().getConfiguration(),
-                        r.getMergedOverrideConfiguration());
+                        proc.getConfiguration(), r.getMergedOverrideConfiguration());
                 r.setLastReportedConfiguration(mergedConfiguration);
 
                 logIfTransactionTooLarge(r.intent, r.icicle);
 
 
                 // Create activity launch transaction.
-                final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
-                        r.appToken);
+                final ClientTransaction clientTransaction = ClientTransaction.obtain(
+                        proc.getThread(), r.appToken);
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
                         // and override configs.
                         mergedConfiguration.getGlobalConfiguration(),
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
-                        r.launchedFromPackage, task.voiceInteractor, app.getReportedProcState(),
+                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                         r.icicle, r.persistentState, results, newIntents,
                         mService.isNextTransitionForward(), profilerInfo));
 
@@ -1526,12 +1446,12 @@
                 // Schedule transaction.
                 mService.getLifecycleManager().scheduleTransaction(clientTransaction);
 
-                if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
+                if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
                         && mService.mHasHeavyWeightFeature) {
                     // This may be a heavy-weight process! Note that the package manager will ensure
                     // that only activity can run in the main process of the .apk, which is the only
                     // thing that will be considered heavy-weight.
-                    if (app.processName.equals(app.info.packageName)) {
+                    if (proc.mName.equals(proc.mInfo.packageName)) {
                         if (mService.mHeavyWeightProcess != null
                                 && mService.mHeavyWeightProcess != proc) {
                             Slog.w(TAG, "Starting new heavy weight process " + proc
@@ -1544,12 +1464,10 @@
 
             } catch (RemoteException e) {
                 if (r.launchFailed) {
-                    // This is the second time we failed -- finish activity
-                    // and give up.
+                    // This is the second time we failed -- finish activity and give up.
                     Slog.e(TAG, "Second failure launching "
-                            + r.intent.getComponent().flattenToShortString()
-                            + ", giving up", e);
-                    mService.mAm.appDiedLocked(app);
+                            + r.intent.getComponent().flattenToShortString() + ", giving up", e);
+                    proc.appDied();
                     stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
                             "2nd-crash", false);
                     return false;
@@ -1658,24 +1576,21 @@
         }
     }
 
-    void startSpecificActivityLocked(ActivityRecord r,
-            boolean andResume, boolean checkConfig) {
+    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
         // Is this activity's application already running?
-        ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName,
-                r.info.applicationInfo.uid, true);
+        final WindowProcessController wpc =
+                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
 
-        if (app != null && app.thread != null) {
+        if (wpc != null && wpc.hasThread()) {
             try {
-                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
+                if ((r.info.flags & ActivityInfo.FLAG_MULTIPROCESS) == 0
                         || !"android".equals(r.info.packageName)) {
-                    // Don't add this if it is a platform component that is marked
-                    // to run in multiple processes, because this is actually
-                    // part of the framework so doesn't make sense to track as a
-                    // separate apk in the process.
-                    app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
-                            mService.mAm.mProcessStats);
+                    // Don't add this if it is a platform component that is marked to run in
+                    // multiple processes, because this is actually part of the framework so doesn't
+                    // make sense to track as a separate apk in the process.
+                    wpc.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode);
                 }
-                realStartActivityLocked(r, app, andResume, checkConfig);
+                realStartActivityLocked(r, wpc, andResume, checkConfig);
                 return;
             } catch (RemoteException e) {
                 Slog.w(TAG, "Exception when starting activity "
@@ -1686,8 +1601,12 @@
             // restart the application.
         }
 
-        mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-                "activity", r.intent.getComponent(), false, false, true);
+        // Post message to start process to avoid possible deadlock of calling into AMS with the
+        // ATMS lock held.
+        final Message msg = PooledLambda.obtainMessage(
+                ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
+                r.info.applicationInfo, true, "activity", r.intent.getComponent());
+        mService.mH.sendMessage(msg);
     }
 
     void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
@@ -1717,24 +1636,25 @@
             sendHint = noResumedActivities || allFocusedProcessesDiffer;
         }
 
-        if (sendHint && mService.mAm.mLocalPowerManager != null) {
-            mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 1);
+        if (sendHint && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
             mPowerHintSent = true;
         }
     }
 
     void sendPowerHintForLaunchEndIfNeeded() {
         // Trigger launch power hint if activity is launched
-        if (mPowerHintSent && mService.mAm.mLocalPowerManager != null) {
-            mService.mAm.mLocalPowerManager.powerHint(PowerHint.LAUNCH, 0);
+        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
             mPowerHintSent = false;
         }
     }
 
-    boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
-            String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask,
-            ProcessRecord callerApp, ActivityRecord resultRecord, ActivityStack resultStack) {
+    boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
+            int requestCode, int callingPid, int callingUid, String callingPackage,
+            boolean ignoreTargetSecurity, boolean launchingInTask,
+            WindowProcessController callerApp, ActivityRecord resultRecord,
+            ActivityStack resultStack) {
         final boolean isCallerRecents = mService.getRecentTasks() != null
                 && mService.getRecentTasks().isCallerRecents(callingUid);
         final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
@@ -2257,9 +2177,9 @@
      * Finish the topmost activities in all stacks that belong to the crashed app.
      * @param app The app that crashed.
      * @param reason Reason to perform this action.
-     * @return The task that was finished in this stack, {@code null} if haven't found any.
+     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
      */
-    TaskRecord finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
+    int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
         TaskRecord finishedTask = null;
         ActivityStack focusedStack = getTopDisplayFocusedStack();
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -2274,7 +2194,7 @@
                 }
             }
         }
-        return finishedTask;
+        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
     }
 
     void finishVoiceTask(IVoiceInteractionSession session) {
@@ -2304,17 +2224,8 @@
             mUserLeaving = true;
         }
 
-        // TODO(b/111363427): The moving-to-top task may not be on the top display, so it could be
-        // different from where the prev activity stays on.
-        final ActivityRecord prev = topRunningActivityLocked();
-
-        if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0
-                || (prev != null && prev.isActivityTypeRecents())) {
-            // Caller wants the home activity moved with it or the previous task is recents in which
-            // case we always return home from the task we are moving to the front.
-            currentStack.getDisplay().moveHomeStackToFront("findTaskToMoveToFront");
-        }
-
+        reason = reason + " findTaskToMoveToFront";
+        boolean reparented = false;
         if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
             final Rect bounds = options.getLaunchBounds();
             task.updateOverrideConfiguration(bounds);
@@ -2322,10 +2233,12 @@
             ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
 
             if (stack != currentStack) {
+                moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
                 task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
-                        "findTaskToMoveToFront");
+                        reason);
                 currentStack = stack;
-                // moveTaskToStackUncheckedLocked() should already placed the task on top,
+                reparented = true;
+                // task.reparent() should already placed the task on top,
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
             if (stack.resizeStackWithLaunchBounds()) {
@@ -2340,6 +2253,10 @@
             }
         }
 
+        if (!reparented) {
+            moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplay(), reason);
+        }
+
         final ActivityRecord r = task.getTopActivity();
         currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
                 r == null ? null : r.appTimeTracker, reason);
@@ -2351,6 +2268,18 @@
                 currentStack, forceNonResizeable);
     }
 
+    private void moveHomeStackToFrontIfNeeded(int flags, ActivityDisplay display, String reason) {
+        final ActivityStack focusedStack = display.getFocusedStack();
+
+        if ((display.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+                && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
+                || (focusedStack != null && focusedStack.isActivityTypeRecents())) {
+            // We move home stack to front when we are on a fullscreen display and caller has
+            // requested the home activity to move with it. Or the previous stack is recents.
+            display.moveHomeStackToFront(reason);
+        }
+    }
+
     boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
         // We use the launch bounds in the activity options is the device supports freeform
         // window management or is launching into the pinned stack.
@@ -2456,7 +2385,7 @@
         }
         if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
             if (r != null) {
-                stack = (T) getValidLaunchStackOnDisplay(displayId, r, options);
+                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options);
                 if (stack != null) {
                     return stack;
                 }
@@ -2521,10 +2450,11 @@
      * If there is no such stack, new dynamic stack can be created.
      * @param displayId Target display.
      * @param r Activity that should be launched there.
+     * @param candidateTask The possible task the activity might be put in.
      * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
      */
     ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable ActivityOptions options) {
+            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options) {
         final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
         if (activityDisplay == null) {
             throw new IllegalArgumentException(
@@ -2535,6 +2465,13 @@
             return null;
         }
 
+        // If {@code r} is already in target display and its task is the same as the candidate task,
+        // the intention should be getting a launch stack for the reusable activity, so we can use
+        // the existing stack.
+        if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
+            return candidateTask.getStack();
+        }
+
         // Return the topmost valid stack on the display.
         for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
             final ActivityStack stack = activityDisplay.getChildAt(i);
@@ -2555,6 +2492,11 @@
         return null;
     }
 
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable ActivityOptions options) {
+        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options);
+    }
+
     // TODO: Can probably be consolidated into getLaunchStack()...
     private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
         switch (stack.getActivityType()) {
@@ -3072,7 +3014,9 @@
         }
 
         // Find any running services associated with this app and stop if needed.
-        mService.mAm.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+        final Message msg = PooledLambda.obtainMessage(ActivityManagerInternal::cleanUpServices,
+                mService.mAmInternal, tr.userId, component, new Intent(tr.getBaseIntent()));
+        mService.mH.sendMessage(msg);
 
         if (!killProcess) {
             return;
@@ -3422,7 +3366,7 @@
                     throw new IllegalStateException("Calling must be system uid");
                 }
                 mLaunchingActivity.release();
-                mService.mAm.mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
+                mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index f6f1e55..20d5ab2 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -22,8 +22,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -65,7 +65,7 @@
  * through the pending activity list, and recording home activity launches.
  */
 public class ActivityStartController {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStartController" : TAG_ATM;
 
     private static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 1;
 
@@ -91,6 +91,8 @@
 
     private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
 
+    boolean mCheckedForSetup = false;
+
     private final class StartHandler extends Handler {
         public StartHandler(Looper looper) {
             super(looper, null, true);
@@ -193,7 +195,7 @@
      */
     void startSetupActivity() {
         // Only do this once per boot.
-        if (mService.mAm.getCheckedForSetup()) {
+        if (mCheckedForSetup) {
             return;
         }
 
@@ -203,7 +205,7 @@
         final ContentResolver resolver = mService.mContext.getContentResolver();
         if (mService.mFactoryTest != FACTORY_TEST_LOW_LEVEL
                 && Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
-            mService.mAm.setCheckedForSetup(true);
+            mCheckedForSetup = true;
 
             // See if we should be showing the platform update setup UI.
             final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
@@ -357,7 +359,7 @@
                             null, userId, ActivityStarter.computeResolveFilterUid(
                                     callingUid, realCallingUid, UserHandle.USER_NULL));
                     // TODO: New, check if this is correct
-                    aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
+                    aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
 
                     if (aInfo != null &&
                             (aInfo.applicationInfo.privateFlags
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 4789ff3..e51824f 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -44,6 +44,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Bundle;
@@ -246,9 +247,9 @@
         if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
             return interceptSuspendedByAdminPackage();
         }
-        final String dialogMessage = pmi.getSuspendedDialogMessage(suspendedPackage, mUserId);
+        final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId);
         mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
-                suspendingPackage, dialogMessage, mUserId);
+                suspendingPackage, dialogInfo, mUserId);
         mCallingPid = mRealCallingPid;
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 33f949f..fa227a2 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -53,25 +53,25 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.am.EventLogTags.AM_NEW_INTENT;
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
@@ -127,7 +127,7 @@
  * an activity and associated task and stack.
  */
 class ActivityStarter {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStarter" : TAG_ATM;
     private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
@@ -590,12 +590,12 @@
         final Bundle verificationBundle
                 = options != null ? options.popAppVerificationBundle() : null;
 
-        ProcessRecord callerApp = null;
+        WindowProcessController callerApp = null;
         if (caller != null) {
-            callerApp = mService.mAm.getRecordForAppLocked(caller);
+            callerApp = mService.getProcessController(caller);
             if (callerApp != null) {
-                callingPid = callerApp.pid;
-                callingUid = callerApp.info.uid;
+                callingPid = callerApp.getPid();
+                callingUid = callerApp.mInfo.uid;
             } else {
                 Slog.w(TAG, "Unable to find app for caller " + caller
                         + " (pid=" + callingPid + ") when starting: "
@@ -726,14 +726,12 @@
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
                 requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
                 inTask != null, callerApp, resultRecord, resultStack);
-        abort |= !mService.mAm.mIntentFirewall.checkStartActivity(intent, callingUid,
+        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
-        final WindowProcessController callerWpc =
-                callerApp != null ? callerApp.getWindowProcessController() : null;
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
-                ? options.getOptions(intent, aInfo, callerWpc, mSupervisor) : null;
+                ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
         if (allowPendingRemoteAnimationRegistryLookup) {
             checkedOptions = mService.getActivityStartController()
                     .getPendingRemoteAnimationRegistry()
@@ -833,8 +831,7 @@
             aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
         }
 
-        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid,
-                callingUid,
+        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                 callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                 resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
                 mSupervisor, checkedOptions, sourceRecord);
@@ -857,7 +854,7 @@
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                     realCallingPid, realCallingUid, "Activity start")) {
                 mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
-                        sourceRecord, startFlags, stack, callerWpc));
+                        sourceRecord, startFlags, stack, callerApp));
                 ActivityOptions.abort(checkedOptions);
                 return ActivityManager.START_SWITCHES_CANCELED;
             }
@@ -874,12 +871,11 @@
     }
 
     private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
-            Intent intent, ProcessRecord callerApp, ActivityRecord r,
+            Intent intent, WindowProcessController callerApp, ActivityRecord r,
             PendingIntentRecord originatingPendingIntent) {
-        boolean callerAppHasForegroundActivity = (callerApp != null)
-                ? callerApp.foregroundActivities
-                : false;
-        if (!mService.mAm.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
+        boolean callerAppHasForegroundActivity =
+                callerApp != null && callerApp.hasForegroundActivities();
+        if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
                 || r == null) {
             // skip logging in this case
             return;
@@ -887,23 +883,23 @@
 
         try {
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "logActivityStart");
-            final int callingUidProcState = mService.mAm.getUidStateLocked(callingUid);
+            final int callingUidProcState = mService.getUidStateLocked(callingUid);
             final boolean callingUidHasAnyVisibleWindow =
                     mService.mWindowManager.isAnyWindowVisibleForUid(callingUid);
             final int realCallingUidProcState = (callingUid == realCallingUid)
                     ? callingUidProcState
-                    : mService.mAm.getUidStateLocked(realCallingUid);
+                    : mService.getUidStateLocked(realCallingUid);
             final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                     ? callingUidHasAnyVisibleWindow
                     : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
             final String targetPackage = r.packageName;
             final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
-            final int targetUidProcState = mService.mAm.getUidStateLocked(targetUid);
+            final int targetUidProcState = mService.getUidStateLocked(targetUid);
             final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
                     ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
                     : false;
             final String targetWhitelistTag = (targetUid != -1)
-                    ? mService.mAm.getPendingTempWhitelistTagForUidLocked(targetUid)
+                    ? mService.getPendingTempWhitelistTagForUidLocked(targetUid)
                     : null;
 
             mSupervisor.getActivityMetricsLogger().logActivityStart(intent, callerApp, r,
@@ -1085,9 +1081,10 @@
                             || !heavy.mName.equals(aInfo.processName))) {
                         int appCallingUid = callingUid;
                         if (caller != null) {
-                            ProcessRecord callerApp = mService.mAm.getRecordForAppLocked(caller);
+                            WindowProcessController callerApp =
+                                    mService.getProcessController(caller);
                             if (callerApp != null) {
-                                appCallingUid = callerApp.info.uid;
+                                appCallingUid = callerApp.mInfo.uid;
                             } else {
                                 Slog.w(TAG, "Unable to find app for caller " + caller
                                         + " (pid=" + callingPid + ") when starting: "
@@ -1127,7 +1124,7 @@
                                         callingUid, realCallingUid, mRequest.filterCallingUid));
                         aInfo = rInfo != null ? rInfo.activityInfo : null;
                         if (aInfo != null) {
-                            aInfo = mService.mAm.getActivityInfoForUser(aInfo, userId);
+                            aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
                         }
                     }
                 }
@@ -1280,6 +1277,7 @@
 
         setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                 voiceInteractor);
+        final int preferredWindowingMode = mLaunchParams.mWindowingMode;
 
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
@@ -1298,25 +1296,6 @@
 
         ActivityRecord reusedActivity = getReusableIntentActivity();
 
-        int preferredWindowingMode = WINDOWING_MODE_UNDEFINED;
-        int preferredLaunchDisplayId = DEFAULT_DISPLAY;
-        if (mOptions != null) {
-            preferredWindowingMode = mOptions.getLaunchWindowingMode();
-            preferredLaunchDisplayId = mOptions.getLaunchDisplayId();
-        }
-
-        // windowing mode and preferred launch display values from {@link LaunchParams} take
-        // priority over those specified in {@link ActivityOptions}.
-        if (!mLaunchParams.isEmpty()) {
-            if (mLaunchParams.hasPreferredDisplay()) {
-                preferredLaunchDisplayId = mLaunchParams.mPreferredDisplayId;
-            }
-
-            if (mLaunchParams.hasWindowingMode()) {
-                preferredWindowingMode = mLaunchParams.mWindowingMode;
-            }
-        }
-
         if (reusedActivity != null) {
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
@@ -1463,7 +1442,7 @@
             // 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.
             mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
-                    preferredLaunchDisplayId, topStack);
+                    mPreferredDisplayId, topStack);
 
             return START_DELIVERED_TO_TOP;
         }
@@ -1493,8 +1472,9 @@
 
         mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
-        mService.mAm.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
-                UserHandle.getAppId(mStartActivity.appInfo.uid), UserHandle.getAppId(mCallingUid));
+        mService.getPackageManagerInternalLocked().grantEphemeralAccess(
+                mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
+                UserHandle.getAppId(mCallingUid));
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
                     mStartActivity.getTask().taskId);
@@ -1542,7 +1522,7 @@
         mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
 
         mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
-                preferredLaunchDisplayId, mTargetStack);
+                mPreferredDisplayId, mTargetStack);
 
         return START_SUCCESS;
     }
@@ -1608,15 +1588,14 @@
 
         mLaunchParams.reset();
 
-        mSupervisor.getLaunchParamsController().calculate(inTask, null /*layout*/, r, sourceRecord,
-                options, mLaunchParams);
+        mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
+                sourceRecord, options, mLaunchParams);
 
         if (mLaunchParams.hasPreferredDisplay()) {
             mPreferredDisplayId = mLaunchParams.mPreferredDisplayId;
         } else {
             mPreferredDisplayId = DEFAULT_DISPLAY;
         }
-        ensureValidPreferredDisplayId(r);
 
         mLaunchMode = r.launchMode;
 
@@ -1709,24 +1688,6 @@
         mNoAnimation = (mLaunchFlags & FLAG_ACTIVITY_NO_ANIMATION) != 0;
     }
 
-    /**
-     * Ensure preferred display ID matches the starting activity.
-     */
-    private void ensureValidPreferredDisplayId(ActivityRecord startingActivity) {
-        // Check if the Activity is a VR activity. If so, the activity should be launched in
-        // main display.
-        if (startingActivity != null && startingActivity.requestedVrComponent != null) {
-            mPreferredDisplayId = DEFAULT_DISPLAY;
-        }
-
-        // Get the virtual display ID from ActivityStackManagerService. If that's set we should
-        // always use that.
-        final int displayId = mService.mVr2dDisplayId;
-        if (displayId != INVALID_DISPLAY) {
-            mPreferredDisplayId = displayId;
-        }
-    }
-
     private void sendNewTaskResultRequestIfNeeded() {
         final ActivityStack sourceStack = mStartActivity.resultTo != null
                 ? mStartActivity.resultTo.getStack() : null;
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java
new file mode 100644
index 0000000..cf72738
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerDebugConfig.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+/**
+ * Common class for the various debug {@link android.util.Log} output configuration relating to
+ * activities.
+ */
+public class ActivityTaskManagerDebugConfig {
+    // All output logs relating to acitvities use the {@link #TAG_ATM} string for tagging their log
+    // output. This makes it easy to identify the origin of the log message when sifting
+    // through a large amount of log output from multiple sources. However, it also makes trying
+    // to figure-out the origin of a log message while debugging the activity manager a little
+    // painful. By setting this constant to true, log messages from the activity manager package
+    // will be tagged with their class names instead fot the generic tag.
+    static final boolean TAG_WITH_CLASS_NAME = false;
+
+    // While debugging it is sometimes useful to have the category name of the log appended to the
+    // base log tag to make sifting through logs with the same base tag easier. By setting this
+    // constant to true, the category name of the log point will be appended to the log tag.
+    private static final boolean APPEND_CATEGORY_NAME = false;
+
+    // Default log tag for the activities.
+    static final String TAG_ATM = "ActivityTaskManager";
+
+    // Enable all debug log categories.
+    static final boolean DEBUG_ALL = false;
+
+    // Enable all debug log categories for activities.
+    private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
+
+    static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
+    static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_FOCUS = false;
+    static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
+    static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
+    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
+    static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
+    static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
+    static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_STACK = DEBUG_ALL || false;
+    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
+    static final boolean DEBUG_TASKS = DEBUG_ALL || false;
+    static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
+    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
+    static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_IDLE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_RELEASE = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
+    static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
+    static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
+    static final boolean DEBUG_CLEANUP = DEBUG_ALL || false;
+    static final boolean DEBUG_METRICS = DEBUG_ALL || false;
+
+    static final String POSTFIX_APP = APPEND_CATEGORY_NAME ? "_App" : "";
+    static final String POSTFIX_IDLE = APPEND_CATEGORY_NAME ? "_Idle" : "";
+    static final String POSTFIX_RELEASE = APPEND_CATEGORY_NAME ? "_Release" : "";
+    static final String POSTFIX_USER_LEAVING = APPEND_CATEGORY_NAME ? "_UserLeaving" : "";
+    static final String POSTFIX_ADD_REMOVE = APPEND_CATEGORY_NAME ? "_AddRemove" : "";
+    static final String POSTFIX_CONFIGURATION = APPEND_CATEGORY_NAME ? "_Configuration" : "";
+    static final String POSTFIX_CONTAINERS = APPEND_CATEGORY_NAME ? "_Containers" : "";
+    static final String POSTFIX_FOCUS = APPEND_CATEGORY_NAME ? "_Focus" : "";
+    static final String POSTFIX_IMMERSIVE = APPEND_CATEGORY_NAME ? "_Immersive" : "";
+    static final String POSTFIX_LOCKTASK = APPEND_CATEGORY_NAME ? "_LockTask" : "";
+    static final String POSTFIX_PAUSE = APPEND_CATEGORY_NAME ? "_Pause" : "";
+    static final String POSTFIX_RECENTS = APPEND_CATEGORY_NAME ? "_Recents" : "";
+    static final String POSTFIX_SAVED_STATE = APPEND_CATEGORY_NAME ? "_SavedState" : "";
+    static final String POSTFIX_STACK = APPEND_CATEGORY_NAME ? "_Stack" : "";
+    static final String POSTFIX_STATES = APPEND_CATEGORY_NAME ? "_States" : "";
+    static final String POSTFIX_SWITCH = APPEND_CATEGORY_NAME ? "_Switch" : "";
+    static final String POSTFIX_TASKS = APPEND_CATEGORY_NAME ? "_Tasks" : "";
+    static final String POSTFIX_TRANSITION = APPEND_CATEGORY_NAME ? "_Transition" : "";
+    static final String POSTFIX_VISIBILITY = APPEND_CATEGORY_NAME ? "_Visibility" : "";
+    static final String POSTFIX_RESULTS = APPEND_CATEGORY_NAME ? "_Results" : "";
+}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index b369b71..02707fb 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.BIND_VOICE_INTERACTION;
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-import static android.Manifest.permission.FILTER_EVENTS;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
@@ -27,10 +26,11 @@
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.Manifest.permission.STOP_APP_SWITCHES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.AppOpsManager.OP_NONE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -38,8 +38,11 @@
 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;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_FACTORY_TEST;
+import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PC;
@@ -50,6 +53,7 @@
 import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
+import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
@@ -67,30 +71,27 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
 import static com.android.server.am.ActivityManagerService.MY_PID;
-import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
-import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG;
 import static com.android.server.am.ActivityManagerService.dumpStackTraces;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.HOME_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto
+        .PREVIOUS_PROC_VISIBLE_TIME_MS;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
+        .MODE;
+import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage
+        .PACKAGE;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
@@ -98,9 +99,26 @@
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
 import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -141,7 +159,7 @@
 import android.app.admin.DevicePolicyCache;
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
-import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -179,6 +197,7 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
@@ -189,6 +208,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
@@ -219,9 +240,12 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.TransferPipe;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AppOpsService;
@@ -230,6 +254,7 @@
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.Watchdog;
+import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.UserManagerService;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.vr.VrManagerInternal;
@@ -237,16 +262,23 @@
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.WindowManagerService;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.lang.ref.WeakReference;
+import java.text.DateFormat;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * System service for managing activities and their containers (task, stacks, displays,... ).
@@ -254,7 +286,7 @@
  * {@hide}
  */
 public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
     private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
@@ -264,30 +296,61 @@
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
     // How long we wait until we timeout on key dispatching.
-    private static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
+    public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
     // How long we wait until we timeout on key dispatching during instrumentation.
-    private static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+    static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+
+    /** Used to indicate that an app transition should be animated. */
+    static final boolean ANIMATE = true;
+
+    /** Hardware-reported OpenGLES version. */
+    final int GL_ES_VERSION;
+
+    public static final String DUMP_ACTIVITIES_CMD = "activities" ;
+    public static final String DUMP_ACTIVITIES_SHORT_CMD = "a" ;
+    public static final String DUMP_LASTANR_CMD = "lastanr" ;
+    public static final String DUMP_LASTANR_TRACES_CMD = "lastanr-traces" ;
+    public static final String DUMP_STARTER_CMD = "starter" ;
+    public static final String DUMP_CONTAINERS_CMD = "containers" ;
+    public static final String DUMP_RECENTS_CMD = "recents" ;
+    public static final String DUMP_RECENTS_SHORT_CMD = "r" ;
+
+    /** This activity is not being relaunched, or being relaunched for a non-resize reason. */
+    public static final int RELAUNCH_REASON_NONE = 0;
+    /** This activity is being relaunched due to windowing mode change. */
+    public static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
+    /** This activity is being relaunched due to a free-resize operation. */
+    public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
 
     Context mContext;
+
     /**
      * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
      * change at runtime. Use mContext for non-UI purposes.
      */
     final Context mUiContext;
+    final ActivityThread mSystemThread;
     H mH;
     UiHandler mUiHandler;
-    ActivityManagerService mAm;
     ActivityManagerInternal mAmInternal;
     UriGrantsManagerInternal mUgmInternal;
     private PackageManagerInternal mPmInternal;
     private ActivityTaskManagerInternal mInternal;
+    PowerManagerInternal mPowerManagerInternal;
+    private UsageStatsManagerInternal mUsageStatsInternal;
+
     PendingIntentController mPendingIntentController;
+    IntentFirewall mIntentFirewall;
+
     /* Global service lock used by the package the owns this service. */
     Object mGlobalLock;
     ActivityStackSupervisor mStackSupervisor;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
     private AppOpsService mAppOpsService;
+    /** All active uids in the system. */
+    private final SparseArray<Integer> mActiveUids = new SparseArray<>();
+    private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
     /** All processes currently running that might have a window organized by name. */
     final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
     /** All processes we currently have running mapped by pid */
@@ -400,6 +463,12 @@
     String mTopData;
 
     /**
+     * Dump of the activity state at the time of the last ANR. Cleared after
+     * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
+     */
+    String mLastANRState;
+
+    /**
      * Used to retain an update lock when the foreground activity is in
      * immersive mode.
      */
@@ -536,8 +605,10 @@
     ActivityTaskManagerService(Context context) {
         mContext = context;
         mFactoryTest = FactoryTest.getMode();
-        mUiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+        mSystemThread = ActivityThread.currentActivityThread();
+        mUiContext = mSystemThread.getSystemUiContext();
         mLifecycleManager = new ClientLifecycleManager();
+        GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
     }
 
     void onSystemReady() {
@@ -551,6 +622,7 @@
     void onInitPowerManagement() {
         mStackSupervisor.initPowerManagement();
         final PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
         mVoiceWakeLock.setReferenceCounted(false);
     }
@@ -637,15 +709,16 @@
     }
 
     // TODO: Will be converted to WM lock once transition is complete.
-    void setActivityManagerService(ActivityManagerService am) {
-        mAm = am;
-        mGlobalLock = mAm;
-        mH = new H(mAm.mHandlerThread.getLooper());
+    void setActivityManagerService(Object globalLock, Looper looper,
+            IntentFirewall intentFirewall, PendingIntentController intentController) {
+        mGlobalLock = globalLock;
+        mH = new H(looper);
         mUiHandler = new UiHandler();
+        mIntentFirewall = intentFirewall;
         final File systemDir = SystemServiceManager.ensureSystemDir();
         mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
         mCompatModePackages = new CompatModePackages(this, systemDir, mH);
-        mPendingIntentController = mAm.mPendingIntentController;
+        mPendingIntentController = intentController;
 
         mTempConfig.setToDefaults();
         mTempConfig.setLocales(LocaleList.getDefault());
@@ -682,6 +755,11 @@
     void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
         mLockTaskController.setWindowManager(wm);
+        mStackSupervisor.setWindowManager(wm);
+    }
+
+    void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
+        mUsageStatsInternal = usageStatsManager;
     }
 
     UserManagerService getUserManager() {
@@ -763,7 +841,7 @@
                     && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) {
                 config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
             }
-            config.reqGlEsVersion = mAm.GL_ES_VERSION;
+            config.reqGlEsVersion = GL_ES_VERSION;
         }
         return config;
     }
@@ -1294,7 +1372,7 @@
                         Slog.i(TAG, "Removing task failed to finish activity");
                     }
                     // Explicitly dismissing the activity so reset its relaunch flag.
-                    r.mRelaunchReason = ActivityRecord.RELAUNCH_REASON_NONE;
+                    r.mRelaunchReason = RELAUNCH_REASON_NONE;
                 } else {
                     res = tr.getStack().requestFinishActivityLocked(token, resultCode,
                             resultData, "app-request", true);
@@ -2594,7 +2672,7 @@
                 pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
                         | Intent.FLAG_ACTIVITY_SINGLE_TOP
                         | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                mAmInternal.closeSystemDialogs("assist");
+                mInternal.closeSystemDialogs("assist");
 
                 try {
                     mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
@@ -2761,8 +2839,7 @@
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
             try {
-                WindowProcessController app =
-                        mAm.getRecordForAppLocked(appInt).getWindowProcessController();
+                final WindowProcessController app = getProcessController(appInt);
                 mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -2783,7 +2860,10 @@
             long ident = Binder.clearCallingIdentity();
             if (mKeyguardShown != keyguardShowing) {
                 mKeyguardShown = keyguardShowing;
-                reportCurKeyguardUsageEventLocked(keyguardShowing);
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
+                        keyguardShowing);
+                mH.sendMessage(msg);
             }
             try {
                 mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
@@ -2920,12 +3000,6 @@
         mTaskChangeNotificationController.unregisterTaskStackListener(listener);
     }
 
-    private void reportCurKeyguardUsageEventLocked(boolean keyguardShowing) {
-        mAm.reportGlobalUsageEventLocked(keyguardShowing
-                ? UsageEvents.Event.KEYGUARD_SHOWN
-                : UsageEvents.Event.KEYGUARD_HIDDEN);
-    }
-
     @Override
     public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
             Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
@@ -3892,8 +3966,9 @@
             }
 
             if (mWindowManager != null) {
-                // Update OOM levels based on display size.
-                mAm.mProcessList.applyDisplaySize(mWindowManager);
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal, displayId);
+                mH.sendMessage(msg);
             }
 
             final long origId = Binder.clearCallingIdentity();
@@ -3921,8 +3996,10 @@
             }
 
             if (mWindowManager != null) {
-                // Update OOM levels based on display size.
-                mAm.mProcessList.applyDisplaySize(mWindowManager);
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::updateOomLevelsForDisplay, mAmInternal,
+                        DEFAULT_DISPLAY);
+                mH.sendMessage(msg);
             }
 
             final long origId = Binder.clearCallingIdentity();
@@ -4129,11 +4206,9 @@
     public void setVrThread(int tid) {
         enforceSystemHasVrFeature();
         synchronized (mGlobalLock) {
-            synchronized (mAm.mPidsSelfLocked) {
-                final int pid = Binder.getCallingPid();
-                final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
-                mVrController.setVrThreadLocked(tid, pid, proc.getWindowProcessController());
-            }
+            final int pid = Binder.getCallingPid();
+            final WindowProcessController wpc = mPidMap.get(pid);
+            mVrController.setVrThreadLocked(tid, pid, wpc);
         }
     }
 
@@ -4150,11 +4225,9 @@
         }
         enforceSystemHasVrFeature();
         synchronized (mGlobalLock) {
-            synchronized (mAm.mPidsSelfLocked) {
-                final int pid = Binder.getCallingPid();
-                final ProcessRecord proc = mAm.mPidsSelfLocked.get(pid);
-                mVrController.setPersistentVrThreadLocked(tid, pid, proc);
-            }
+            final int pid = Binder.getCallingPid();
+            final WindowProcessController proc = mPidMap.get(pid);
+            mVrController.setPersistentVrThreadLocked(tid, pid, proc);
         }
     }
 
@@ -4262,6 +4335,17 @@
         }
     }
 
+    public static String relaunchReasonToString(int relaunchReason) {
+        switch (relaunchReason) {
+            case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
+                return "window_resize";
+            case RELAUNCH_REASON_FREE_RESIZE:
+                return "free_resize";
+            default:
+                return null;
+        }
+    }
+
     ActivityStack getTopDisplayFocusedStack() {
         return mStackSupervisor.getTopDisplayFocusedStack();
     }
@@ -4286,16 +4370,177 @@
                 || transit == TRANSIT_TASK_TO_FRONT;
     }
 
-    void dumpSleepStates(PrintWriter pw, boolean testPssMode) {
-        synchronized (mGlobalLock) {
-            pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
-            if (mRunningVoice != null) {
-                pw.println("  mRunningVoice=" + mRunningVoice);
-                pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
+    void dumpLastANRLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
+        if (mLastANRState == null) {
+            pw.println("  <no ANR has occurred since boot>");
+        } else {
+            pw.println(mLastANRState);
+        }
+    }
+
+    void dumpLastANRTracesLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER LAST ANR TRACES (dumpsys activity lastanr-traces)");
+
+        final File[] files = new File(ANR_TRACE_DIR).listFiles();
+        if (ArrayUtils.isEmpty(files)) {
+            pw.println("  <no ANR has occurred since boot>");
+            return;
+        }
+        // Find the latest file.
+        File latest = null;
+        for (File f : files) {
+            if ((latest == null) || (latest.lastModified() < f.lastModified())) {
+                latest = f;
             }
-            pw.println("  mSleeping=" + mSleeping);
-            pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
-            pw.println("  mVrController=" + mVrController);
+        }
+        pw.print("File: ");
+        pw.print(latest.getName());
+        pw.println();
+        try (BufferedReader in = new BufferedReader(new FileReader(latest))) {
+            String line;
+            while ((line = in.readLine()) != null) {
+                pw.println(line);
+            }
+        } catch (IOException e) {
+            pw.print("Unable to read: ");
+            pw.print(e);
+            pw.println();
+        }
+    }
+
+    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
+        dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
+                "ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
+    }
+
+    void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
+        pw.println(header);
+
+        boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+                dumpPackage);
+        boolean needSep = printedAnything;
+
+        boolean printed = ActivityStackSupervisor.printThisActivity(pw,
+                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
+                "  ResumedActivity: ");
+        if (printed) {
+            printedAnything = true;
+            needSep = false;
+        }
+
+        if (dumpPackage == null) {
+            if (needSep) {
+                pw.println();
+            }
+            printedAnything = true;
+            mStackSupervisor.dump(pw, "  ");
+        }
+
+        if (!printedAnything) {
+            pw.println("  (nothing)");
+        }
+    }
+
+    void dumpActivityContainersLocked(PrintWriter pw) {
+        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
+        mStackSupervisor.dumpChildrenNames(pw, " ");
+        pw.println(" ");
+    }
+
+    void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
+        getActivityStartController().dump(pw, "", dumpPackage);
+    }
+
+    /**
+     * There are three things that cmd can be:
+     *  - a flattened component name that matches an existing activity
+     *  - the cmd arg isn't the flattened component name of an existing activity:
+     *    dump all activity whose component contains the cmd as a substring
+     *  - A hex number of the ActivityRecord object instance.
+     *
+     *  @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack
+     *  @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack
+     */
+    protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+            int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) {
+        ArrayList<ActivityRecord> activities;
+
+        synchronized (mGlobalLock) {
+            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+                    dumpFocusedStackOnly);
+        }
+
+        if (activities.size() <= 0) {
+            return false;
+        }
+
+        String[] newArgs = new String[args.length - opti];
+        System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
+        TaskRecord lastTask = null;
+        boolean needSep = false;
+        for (int i = activities.size() - 1; i >= 0; i--) {
+            ActivityRecord r = activities.get(i);
+            if (needSep) {
+                pw.println();
+            }
+            needSep = true;
+            synchronized (mGlobalLock) {
+                final TaskRecord task = r.getTask();
+                if (lastTask != task) {
+                    lastTask = task;
+                    pw.print("TASK "); pw.print(lastTask.affinity);
+                    pw.print(" id="); pw.print(lastTask.taskId);
+                    pw.print(" userId="); pw.println(lastTask.userId);
+                    if (dumpAll) {
+                        lastTask.dump(pw, "  ");
+                    }
+                }
+            }
+            dumpActivity("  ", fd, pw, activities.get(i), newArgs, dumpAll);
+        }
+        return true;
+    }
+
+    /**
+     * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
+     * there is a thread associated with the activity.
+     */
+    private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
+            final ActivityRecord r, String[] args, boolean dumpAll) {
+        String innerPrefix = prefix + "  ";
+        synchronized (mGlobalLock) {
+            pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
+            pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
+            pw.print(" pid=");
+            if (r.hasProcess()) pw.println(r.app.getPid());
+            else pw.println("(not running)");
+            if (dumpAll) {
+                r.dump(pw, innerPrefix);
+            }
+        }
+        if (r.attachedToProcess()) {
+            // flush anything that is already in the PrintWriter since the thread is going
+            // to write to the file descriptor directly
+            pw.flush();
+            try {
+                TransferPipe tp = new TransferPipe();
+                try {
+                    r.app.getThread().dumpActivity(tp.getWriteFd(),
+                            r.appToken, innerPrefix, args);
+                    tp.go(fd);
+                } finally {
+                    tp.kill();
+                }
+            } catch (IOException e) {
+                pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+            } catch (RemoteException e) {
+                pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+            }
         }
     }
 
@@ -4428,14 +4673,6 @@
         return kept;
     }
 
-    /**
-     * Returns true if this configuration change is interesting enough to send an
-     * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
-     */
-    private static boolean isSplitConfigurationChange(int configDiff) {
-        return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
-    }
-
     /** Update default (global) configuration and notify listeners about changes. */
     private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
             boolean persistent, int userId, boolean deferResume) {
@@ -4486,8 +4723,11 @@
             SystemProperties.set("persist.sys.locale",
                     locales.get(bestLocaleIndex).toLanguageTag());
             LocaleList.setDefault(locales, bestLocaleIndex);
-            mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
-                    locales.get(bestLocaleIndex)));
+
+            final Message m = PooledLambda.obtainMessage(
+                    ActivityTaskManagerService::sendLocaleToMountDaemonMsg, this,
+                    locales.get(bestLocaleIndex));
+            mH.sendMessage(m);
         }
 
         mTempConfig.seq = increaseConfigurationSeqLocked();
@@ -4497,8 +4737,7 @@
 
         Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
         // TODO(multi-display): Update UsageEvents#Event to include displayId.
-        mAm.mUsageStatsService.reportConfigurationChange(
-                mTempConfig, mAmInternal.getCurrentUserId());
+        mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());
 
         // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
         updateShouldShowDialogsLocked(mTempConfig);
@@ -4512,20 +4751,21 @@
         // to retrieve resource values after we return will be sure to get the new ones. This is
         // especially important during boot, where the first config change needs to guarantee all
         // resources have that config before following boot code is executed.
-        mAm.mSystemThread.applyConfigurationToResources(mTempConfig);
+        mSystemThread.applyConfigurationToResources(mTempConfig);
 
         // We need another copy of global config because we're scheduling some calls instead of
         // running them in place. We need to be sure that object we send will be handled unchanged.
         final Configuration configCopy = new Configuration(mTempConfig);
         if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
-            Message msg = mAm.mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
-            msg.obj = configCopy;
-            msg.arg1 = userId;
-            mAm.mHandler.sendMessage(msg);
+            final Message msg = PooledLambda.obtainMessage(
+                    ActivityTaskManagerService::sendPutConfigurationForUserMsg,
+                    this, userId, configCopy);
+            mH.sendMessage(msg);
         }
 
         for (int i = mPidMap.size() - 1; i >= 0; i--) {
-            WindowProcessController app = mPidMap.get(mPidMap.keyAt(i));
+            final int pid = mPidMap.keyAt(i);
+            final WindowProcessController app = mPidMap.get(pid);
             if (DEBUG_CONFIGURATION) {
                 Slog.v(TAG_CONFIGURATION, "Update process config of "
                         + app.mName + " to new config " + configCopy);
@@ -4533,38 +4773,10 @@
             app.onConfigurationChanged(configCopy);
         }
 
-        Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING
-                | Intent.FLAG_RECEIVER_FOREGROUND
-                | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-        mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                UserHandle.USER_ALL);
-        if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
-            intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-            if (initLocale || !mAm.mProcessesReady) {
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            }
-            mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
-                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
-                    UserHandle.USER_ALL);
-        }
-
-        // Send a broadcast to PackageInstallers if the configuration change is interesting
-        // for the purposes of installing additional splits.
-        if (!initLocale && isSplitConfigurationChange(changes)) {
-            intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
-                    | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-
-            // Typically only app stores will have this permission.
-            String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
-            mAm.broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
-                    OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
-        }
+        final Message msg = PooledLambda.obtainMessage(
+                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
+                mAmInternal, changes, initLocale);
+        mH.sendMessage(msg);
 
         // Override configuration of the default display duplicates global config, so we need to
         // update it also. This will also notify WindowManager about changes.
@@ -4633,8 +4845,12 @@
             if (isDensityChange && displayId == DEFAULT_DISPLAY) {
                 mAppWarnings.onDensityChanged();
 
-                mAm.killAllBackgroundProcessesExcept(N,
-                        ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                // Post message to start process to avoid possible deadlock of calling into AMS with
+                // the ATMS lock held.
+                final Message msg = PooledLambda.obtainMessage(
+                        ActivityManagerInternal::killAllBackgroundProcessesExcept, mAmInternal,
+                        N, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+                mH.sendMessage(msg);
             }
         }
 
@@ -4659,6 +4875,26 @@
         mWindowManager.setEventDispatching(booted && !mShuttingDown);
     }
 
+    private void sendPutConfigurationForUserMsg(int userId, Configuration config) {
+        final ContentResolver resolver = mContext.getContentResolver();
+        Settings.System.putConfigurationForUser(resolver, config, userId);
+    }
+
+    private void sendLocaleToMountDaemonMsg(Locale l) {
+        try {
+            IBinder service = ServiceManager.getService("mount");
+            IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
+            Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
+            storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error storing locale for decryption UI", e);
+        }
+    }
+
+    boolean isActivityStartsLoggingEnabled() {
+        return mAmInternal.isActivityStartsLoggingEnabled();
+    }
+
     void enableScreenAfterBoot(boolean booted) {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                 SystemClock.uptimeMillis());
@@ -4686,70 +4922,7 @@
     }
 
     private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
-        if (r != null && (r.isInstrumenting() || r.isUsingWrapper())) {
-            return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
-        }
-        return KEY_DISPATCHING_TIMEOUT_MS;
-    }
-
-    long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
-        if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission " + FILTER_EVENTS);
-        }
-        WindowProcessController proc;
-        long timeout;
-        synchronized (mGlobalLock) {
-            proc = mPidMap.get(pid);
-            timeout = getInputDispatchingTimeoutLocked(proc);
-        }
-
-        if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
-            return -1;
-        }
-
-        return timeout;
-    }
-
-    /**
-     * Handle input dispatching timeouts.
-     * Returns whether input dispatching should be aborted or not.
-     */
-    boolean inputDispatchingTimedOut(final WindowProcessController proc,
-            final ActivityRecord activity, final ActivityRecord parent,
-            final boolean aboveSystem, String reason) {
-        if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires permission " + FILTER_EVENTS);
-        }
-
-        final String annotation;
-        if (reason == null) {
-            annotation = "Input dispatching timed out";
-        } else {
-            annotation = "Input dispatching timed out (" + reason + ")";
-        }
-
-        if (proc != null) {
-            synchronized (mGlobalLock) {
-                if (proc.isDebugging()) {
-                    return false;
-                }
-
-                if (proc.isInstrumenting()) {
-                    Bundle info = new Bundle();
-                    info.putString("shortMsg", "keyDispatchingTimedOut");
-                    info.putString("longMsg", annotation);
-                    mAm.finishInstrumentationLocked(
-                            (ProcessRecord) proc.mOwner, Activity.RESULT_CANCELED, info);
-                    return true;
-                }
-            }
-            mH.post(() -> {
-                mAm.mAppErrors.appNotResponding(
-                        (ProcessRecord) proc.mOwner, activity, parent, aboveSystem, annotation);
-            });
-        }
-
-        return true;
+        return r != null ? r.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
     }
 
     /**
@@ -5245,7 +5418,7 @@
         return newInfo;
     }
 
-    private WindowProcessController getProcessController(String processName, int uid) {
+    WindowProcessController getProcessController(String processName, int uid) {
         if (uid == SYSTEM_UID) {
             // The system gets to run in any process. If there are multiple processes with the same
             // uid, just pick the first (this should never happen).
@@ -5266,6 +5439,38 @@
         return mProcessNames.get(processName, uid);
     }
 
+    WindowProcessController getProcessController(IApplicationThread thread) {
+        if (thread == null) {
+            return null;
+        }
+
+        final IBinder threadBinder = thread.asBinder();
+        final ArrayMap<String, SparseArray<WindowProcessController>> pmap = mProcessNames.getMap();
+        for (int i = pmap.size()-1; i >= 0; i--) {
+            final SparseArray<WindowProcessController> procs = pmap.valueAt(i);
+            for (int j = procs.size() - 1; j >= 0; j--) {
+                final WindowProcessController proc = procs.valueAt(j);
+                if (proc.hasThread() && proc.getThread().asBinder() == threadBinder) {
+                    return proc;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    int getUidStateLocked(int uid) {
+        return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT);
+    }
+
+    /**
+     * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on
+     * the whitelist
+     */
+    String getPendingTempWhitelistTagForUidLocked(int uid) {
+        return mPendingTempWhitelist.get(uid);
+    }
+
     void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
         if (true || Build.IS_USER) {
             return;
@@ -5327,6 +5532,8 @@
 
     final class H extends Handler {
         static final int REPORT_TIME_TRACKER_MSG = 1;
+        static final int FIRST_ACTIVITY_STACK_MSG = 100;
+        static final int FIRST_SUPERVISOR_STACK_MSG = 200;
 
         public H(Looper looper) {
             super(looper, null, true);
@@ -5673,6 +5880,9 @@
         @Override
         public void finishHeavyWeightApp() {
             synchronized (mGlobalLock) {
+                if (mHeavyWeightProcess != null) {
+                    mHeavyWeightProcess.finishActivities();
+                }
                 ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(
                         mHeavyWeightProcess);
             }
@@ -5763,14 +5973,6 @@
         }
 
         @Override
-        public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
-            synchronized (mGlobalLock) {
-                return ActivityTaskManagerService.this.inputDispatchingTimedOut(
-                        pid, aboveSystem, reason);
-            }
-        }
-
-        @Override
         public void onProcessMapped(int pid, WindowProcessController proc) {
             synchronized (mGlobalLock) {
                 mPidMap.put(pid, proc);
@@ -5808,6 +6010,13 @@
         }
 
         @Override
+        public void onPackageReplaced(ApplicationInfo aInfo) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+            }
+        }
+
+        @Override
         public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
             synchronized (mGlobalLock) {
                 return compatibilityInfoForPackageLocked(ai);
@@ -5822,7 +6031,7 @@
          * @param displayId The ID of the display showing the IME.
          */
         @Override
-        public void onImeWindowSetOnDisplay(int pid, int displayId) {
+        public void onImeWindowSetOnDisplay(final int pid, final int displayId) {
             if (pid == MY_PID || pid < 0) {
                 if (DEBUG_CONFIGURATION) {
                     Slog.w(TAG,
@@ -5832,29 +6041,28 @@
             }
             mH.post(() -> {
                 synchronized (mGlobalLock) {
-                    // Check if display is initialized in AM.
-                    if (!mStackSupervisor.isDisplayAdded(displayId)) {
-                        // Call come when display is not yet added or has already been removed.
+                    final ActivityDisplay activityDisplay =
+                            mStackSupervisor.getActivityDisplay(displayId);
+                    if (activityDisplay == null) {
+                        // Call might come when display is not yet added or has been removed.
                         if (DEBUG_CONFIGURATION) {
                             Slog.w(TAG, "Trying to update display configuration for non-existing "
-                                            + "displayId=" + displayId);
+                                    + "displayId=" + displayId);
                         }
                         return;
                     }
-                    final WindowProcessController imeProcess = mPidMap.get(pid);
-                    if (imeProcess == null) {
+                    final WindowProcessController process = mPidMap.get(pid);
+                    if (process == null) {
                         if (DEBUG_CONFIGURATION) {
-                            Slog.w(TAG, "Trying to update display configuration for invalid pid: "
-                                            + pid);
+                            Slog.w(TAG, "Trying to update display configuration for invalid "
+                                    + "process, pid=" + pid);
                         }
                         return;
                     }
-                    // Fetch the current override configuration of the display and set it to the
-                    // process global configuration.
-                    imeProcess.onConfigurationChanged(
-                            mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+                    process.registerDisplayConfigurationListenerLocked(activityDisplay);
                 }
             });
+
         }
 
         @Override
@@ -5971,9 +6179,515 @@
                 mUiHandler.post(() -> {
                     Dialog d = new FactoryErrorDialog(mUiContext, errorMsg);
                     d.show();
-                    mAm.ensureBootCompleted();
+                    mAmInternal.ensureBootCompleted();
                 });
             }
         }
+
+        @Override
+        public void handleAppDied(WindowProcessController wpc, boolean restarting,
+                Runnable finishInstrumentationCallback) {
+            synchronized (mGlobalLock) {
+                // Remove this application's activities from active lists.
+                boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+
+                wpc.clearRecentTasks();
+                wpc.clearActivities();
+
+                if (wpc.isInstrumenting()) {
+                    finishInstrumentationCallback.run();
+                }
+
+                mWindowManager.deferSurfaceLayout();
+                try {
+                    if (!restarting && hasVisibleActivities
+                            && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+                        // If there was nothing to resume, and we are not already restarting this
+                        // process, but there is a visible activity that is hosted by the process...
+                        // then make sure all visible activities are running, taking care of
+                        // restarting this process.
+                        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    }
+                } finally {
+                    mWindowManager.continueSurfaceLayout();
+                }
+            }
+        }
+
+        @Override
+        public void closeSystemDialogs(String reason) {
+            enforceNotIsolatedCaller("closeSystemDialogs");
+
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                synchronized (mGlobalLock) {
+                    // Only allow this from foreground processes, so that background
+                    // applications can't abuse it to prevent system UI from being shown.
+                    if (uid >= FIRST_APPLICATION_UID) {
+                        final WindowProcessController proc = mPidMap.get(pid);
+                        if (!proc.isPerceptible()) {
+                            Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+                                    + " from background process " + proc);
+                            return;
+                        }
+                    }
+                    mWindowManager.closeSystemDialogs(reason);
+
+                    mStackSupervisor.closeSystemDialogsLocked();
+                }
+                // Call into AM outside the synchronized block.
+                mAmInternal.broadcastCloseSystemDialogs(reason);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+        @Override
+        public void cleanupDisabledPackageComponents(
+                String packageName, Set<String> disabledClasses, int userId, boolean booted) {
+            synchronized (mGlobalLock) {
+                // Clean-up disabled activities.
+                if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+                        packageName, disabledClasses, true, false, userId) && booted) {
+                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mStackSupervisor.scheduleIdleLocked();
+                }
+
+                // Clean-up disabled tasks
+                getRecentTasks().cleanupDisabledPackageTasksLocked(
+                        packageName, disabledClasses, userId);
+            }
+        }
+
+        @Override
+        public boolean onForceStopPackage(String packageName, boolean doit, boolean evenPersistent,
+                int userId) {
+            synchronized (mGlobalLock) {
+
+                boolean didSomething =
+                        getActivityStartController().clearPendingActivityLaunches(packageName);
+                didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+                        null, doit, evenPersistent, userId);
+                return didSomething;
+            }
+        }
+
+        @Override
+        public void resumeTopActivities(boolean scheduleIdle) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                if (scheduleIdle) {
+                    mStackSupervisor.scheduleIdleLocked();
+                }
+            }
+        }
+
+        @Override
+        public void preBindApplication(WindowProcessController wpc) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
+            }
+        }
+
+        @Override
+        public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.attachApplicationLocked(wpc);
+            }
+        }
+
+        @Override
+        public void notifyLockedProfile(@UserIdInt int userId, int currentUserId) {
+            try {
+                if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
+                    throw new SecurityException("Only privileged app can call notifyLockedProfile");
+                }
+            } catch (RemoteException ex) {
+                throw new SecurityException("Fail to check is caller a privileged app", ex);
+            }
+
+            synchronized (mGlobalLock) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    if (mAmInternal.shouldConfirmCredentials(userId)) {
+                        if (mKeyguardController.isKeyguardLocked()) {
+                            // Showing launcher to avoid user entering credential twice.
+                            startHomeActivity(currentUserId, "notifyLockedProfile");
+                        }
+                        mStackSupervisor.lockAllProfileTasks(userId);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+
+        @Override
+        public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
+            mAmInternal.enforceCallingPermission(
+                    MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
+
+            synchronized (mGlobalLock) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                            FLAG_ACTIVITY_TASK_ON_HOME);
+                    ActivityOptions activityOptions = options != null
+                            ? new ActivityOptions(options) : ActivityOptions.makeBasic();
+                    activityOptions.setLaunchTaskId(
+                            mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
+                    mContext.startActivityAsUser(intent, activityOptions.toBundle(),
+                            UserHandle.CURRENT);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+
+        @Override
+        public void writeActivitiesToProto(ProtoOutputStream proto) {
+            synchronized (mGlobalLock) {
+                // The output proto of "activity --proto activities"
+                // is ActivityManagerServiceDumpActivitiesProto
+                mStackSupervisor.writeToProto(proto,
+                        ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
+            }
+        }
+
+        @Override
+        public void saveANRState(String reason) {
+            synchronized (mGlobalLock) {
+                final StringWriter sw = new StringWriter();
+                final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+                pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
+                if (reason != null) {
+                    pw.println("  Reason: " + reason);
+                }
+                pw.println();
+                getActivityStartController().dump(pw, "  ", null);
+                pw.println();
+                pw.println("-------------------------------------------------------------------------------");
+                dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
+                        true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
+                        "" /* header */);
+                pw.println();
+                pw.close();
+
+                mLastANRState = sw.toString();
+            }
+        }
+
+        @Override
+        public void clearSavedANRState() {
+            synchronized (mGlobalLock) {
+                mLastANRState = null;
+            }
+        }
+
+        @Override
+        public void dump(String cmd, FileDescriptor fd, PrintWriter pw, String[] args, int opti,
+                boolean dumpAll, boolean dumpClient, String dumpPackage) {
+            synchronized (mGlobalLock) {
+                if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd)) {
+                    dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
+                } else if (DUMP_LASTANR_CMD.equals(cmd)) {
+                    dumpLastANRLocked(pw);
+                } else if (DUMP_LASTANR_TRACES_CMD.equals(cmd)) {
+                    dumpLastANRTracesLocked(pw);
+                } else if (DUMP_STARTER_CMD.equals(cmd)) {
+                    dumpActivityStarterLocked(pw, dumpPackage);
+                } else if (DUMP_CONTAINERS_CMD.equals(cmd)) {
+                    dumpActivityContainersLocked(pw);
+                } else if (DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) {
+                    if (getRecentTasks() != null) {
+                        getRecentTasks().dump(pw, dumpAll, dumpPackage);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean dumpForProcesses(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+                String dumpPackage, int dumpAppId, boolean needSep, boolean testPssMode,
+                int wakefulness) {
+            synchronized (mGlobalLock) {
+                if (mHomeProcess != null && (dumpPackage == null
+                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    pw.println("  mHomeProcess: " + mHomeProcess);
+                }
+                if (mPreviousProcess != null && (dumpPackage == null
+                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    pw.println("  mPreviousProcess: " + mPreviousProcess);
+                }
+                if (dumpAll && (mPreviousProcess == null || dumpPackage == null
+                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                    StringBuilder sb = new StringBuilder(128);
+                    sb.append("  mPreviousProcessVisibleTime: ");
+                    TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
+                    pw.println(sb);
+                }
+                if (mHeavyWeightProcess != null && (dumpPackage == null
+                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+                    if (needSep) {
+                        pw.println();
+                        needSep = false;
+                    }
+                    pw.println("  mHeavyWeightProcess: " + mHeavyWeightProcess);
+                }
+                if (dumpPackage == null) {
+                    pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
+                    mStackSupervisor.dumpDisplayConfigs(pw, "  ");
+                }
+                if (dumpAll) {
+                    if (dumpPackage == null) {
+                        pw.println("  mConfigWillChange: "
+                                + getTopDisplayFocusedStack().mConfigWillChange);
+                    }
+                    if (mCompatModePackages.getPackages().size() > 0) {
+                        boolean printed = false;
+                        for (Map.Entry<String, Integer> entry
+                                : mCompatModePackages.getPackages().entrySet()) {
+                            String pkg = entry.getKey();
+                            int mode = entry.getValue();
+                            if (dumpPackage != null && !dumpPackage.equals(pkg)) {
+                                continue;
+                            }
+                            if (!printed) {
+                                pw.println("  mScreenCompatPackages:");
+                                printed = true;
+                            }
+                            pw.println("    " + pkg + ": " + mode);
+                        }
+                    }
+                }
+
+                if (dumpPackage == null) {
+                    pw.println("  mWakefulness="
+                            + PowerManagerInternal.wakefulnessToString(wakefulness));
+                    pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
+                    if (mRunningVoice != null) {
+                        pw.println("  mRunningVoice=" + mRunningVoice);
+                        pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
+                    }
+                    pw.println("  mSleeping=" + mSleeping);
+                    pw.println("  mShuttingDown=" + mShuttingDown + " mTestPssMode=" + testPssMode);
+                    pw.println("  mVrController=" + mVrController);
+                }
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.dumpWithHeader(pw, "  ", true);
+                }
+                if (mAllowAppSwitchUids.size() > 0) {
+                    boolean printed = false;
+                    for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
+                        ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i);
+                        for (int j = 0; j < types.size(); j++) {
+                            if (dumpPackage == null ||
+                                    UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
+                                if (needSep) {
+                                    pw.println();
+                                    needSep = false;
+                                }
+                                if (!printed) {
+                                    pw.println("  mAllowAppSwitchUids:");
+                                    printed = true;
+                                }
+                                pw.print("    User ");
+                                pw.print(mAllowAppSwitchUids.keyAt(i));
+                                pw.print(": Type ");
+                                pw.print(types.keyAt(j));
+                                pw.print(" = ");
+                                UserHandle.formatUid(pw, types.valueAt(j).intValue());
+                                pw.println();
+                            }
+                        }
+                    }
+                }
+                if (dumpPackage == null) {
+                    if (mController != null) {
+                        pw.println("  mController=" + mController
+                                + " mControllerIsAMonkey=" + mControllerIsAMonkey);
+                    }
+                    pw.println("  mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
+                    pw.println("  mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
+                }
+
+                return needSep;
+            }
+        }
+
+        @Override
+        public void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage) {
+            synchronized (mGlobalLock) {
+                if (dumpPackage == null) {
+                    getGlobalConfiguration().writeToProto(proto, GLOBAL_CONFIGURATION);
+                    proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
+                    writeSleepStateToProto(proto);
+                    if (mController != null) {
+                        final long token = proto.start(CONTROLLER);
+                        proto.write(CONTROLLER, mController.toString());
+                        proto.write(IS_A_MONKEY, mControllerIsAMonkey);
+                        proto.end(token);
+                    }
+                    mStackSupervisor.mGoingToSleep.writeToProto(proto, GOING_TO_SLEEP);
+                    mStackSupervisor.mLaunchingActivity.writeToProto(proto, LAUNCHING_ACTIVITY);
+                }
+
+                if (mHomeProcess != null && (dumpPackage == null
+                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
+                    mHomeProcess.writeToProto(proto, HOME_PROC);
+                }
+
+                if (mPreviousProcess != null && (dumpPackage == null
+                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                    mPreviousProcess.writeToProto(proto, PREVIOUS_PROC);
+                    proto.write(PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
+                }
+
+                if (mHeavyWeightProcess != null && (dumpPackage == null
+                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+                    mHeavyWeightProcess.writeToProto(proto, HEAVY_WEIGHT_PROC);
+                }
+
+                for (Map.Entry<String, Integer> entry
+                        : mCompatModePackages.getPackages().entrySet()) {
+                    String pkg = entry.getKey();
+                    int mode = entry.getValue();
+                    if (dumpPackage == null || dumpPackage.equals(pkg)) {
+                        long compatToken = proto.start(SCREEN_COMPAT_PACKAGES);
+                        proto.write(PACKAGE, pkg);
+                        proto.write(MODE, mode);
+                        proto.end(compatToken);
+                    }
+                }
+
+                if (mCurAppTimeTracker != null) {
+                    mCurAppTimeTracker.writeToProto(proto, CURRENT_TRACKER, true);
+                }
+
+            }
+        }
+
+        @Override
+        public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
+                String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
+                boolean dumpFocusedStackOnly) {
+            synchronized (mGlobalLock) {
+                return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti,
+                        dumpAll, dumpVisibleStacksOnly, dumpFocusedStackOnly);
+            }
+        }
+
+        @Override
+        public boolean canGcNow() {
+            synchronized (mGlobalLock) {
+                return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+            }
+        }
+
+        @Override
+        public WindowProcessController getTopApp() {
+            synchronized (mGlobalLock) {
+                final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+                return top != null ? top.app : null;
+            }
+        }
+
+        @Override
+        public void rankTaskLayersIfNeeded() {
+            synchronized (mGlobalLock) {
+                if (mStackSupervisor != null) {
+                    mStackSupervisor.rankTaskLayersIfNeeded();
+                }
+            }
+        }
+
+        @Override
+        public void scheduleDestroyAllActivities(String reason) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+            }
+        }
+
+        @Override
+        public void removeUser(int userId) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.removeUserLocked(userId);
+            }
+        }
+
+        @Override
+        public boolean switchUser(int userId, UserState userState) {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.switchUserLocked(userId, userState);
+            }
+        }
+
+        @Override
+        public void onHandleAppCrash(WindowProcessController wpc) {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.handleAppCrashLocked(wpc);
+            }
+        }
+
+        @Override
+        public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid, int procState) {
+            synchronized (mGlobalLock) {
+                mActiveUids.put(uid, procState);
+            }
+        }
+
+        @Override
+        public void onUidInactive(int uid) {
+            synchronized (mGlobalLock) {
+                mActiveUids.remove(uid);
+            }
+        }
+
+        @Override
+        public void onActiveUidsCleared() {
+            synchronized (mGlobalLock) {
+                mActiveUids.clear();
+            }
+        }
+
+        @Override
+        public void onUidProcStateChanged(int uid, int procState) {
+            synchronized (mGlobalLock) {
+                if (mActiveUids.get(uid) != null) {
+                    mActiveUids.put(uid, procState);
+                }
+            }
+        }
+
+        @Override
+        public void onUidAddedToPendingTempWhitelist(int uid, String tag) {
+            synchronized (mGlobalLock) {
+                mPendingTempWhitelist.put(uid, tag);
+            }
+        }
+
+        @Override
+        public void onUidRemovedFromPendingTempWhitelist(int uid) {
+            synchronized (mGlobalLock) {
+                mPendingTempWhitelist.remove(uid);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index cde633d..a80a5b5 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -63,7 +65,7 @@
         mService = service;
         mProc = data.proc;
         mResult = data.result;
-        mIsRestartable = (data.task != null || data.isRestartableForService)
+        mIsRestartable = (data.taskId != INVALID_TASK_ID || data.isRestartableForService)
                 && Settings.Global.getInt(context.getContentResolver(),
                 Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, 0) != 0;
         BidiFormatter bidi = BidiFormatter.getInstance();
@@ -209,7 +211,7 @@
 
     static class Data {
         AppErrorResult result;
-        TaskRecord task;
+        int taskId;
         boolean repeating;
         ProcessRecord proc;
         boolean isRestartableForService;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6a9c887..e8ec057 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -16,12 +16,13 @@
 
 package com.android.server.am;
 
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -46,21 +47,17 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.app.ProcessMap;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.os.ProcessCpuTracker;
 import com.android.server.RescueParty;
 import com.android.server.Watchdog;
 
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Set;
 
@@ -124,7 +121,7 @@
                 proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
                     }
@@ -151,7 +148,7 @@
                 proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
@@ -184,7 +181,7 @@
                 final int uidCount = uids.size();
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
@@ -214,7 +211,7 @@
                 final int uidCount = uids.size();
                 for (int i = 0; i < uidCount; i++) {
                     final int puid = uids.keyAt(i);
-                    final ProcessRecord r = mService.mProcessNames.get(pname, puid);
+                    final ProcessRecord r = mService.getProcessNames().get(pname, puid);
                     if (dumpPackage != null && (r == null
                             || !r.pkgList.containsKey(dumpPackage))) {
                         continue;
@@ -411,11 +408,10 @@
         }
 
         final int relaunchReason = r != null
-                ? r.getWindowProcessController().computeRelaunchReason()
-                : ActivityRecord.RELAUNCH_REASON_NONE;
+                ? r.getWindowProcessController().computeRelaunchReason() : RELAUNCH_REASON_NONE;
 
         AppErrorResult result = new AppErrorResult();
-        TaskRecord task;
+        int taskId;
         synchronized (mService) {
             /**
              * If crash is handled by instance of {@link android.app.IActivityController},
@@ -428,7 +424,7 @@
 
             // Suppress crash dialog if the process is being relaunched due to a crash during a free
             // resize.
-            if (relaunchReason == ActivityRecord.RELAUNCH_REASON_FREE_RESIZE) {
+            if (relaunchReason == RELAUNCH_REASON_FREE_RESIZE) {
                 return;
             }
 
@@ -458,7 +454,7 @@
             final Message msg = Message.obtain();
             msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
 
-            task = data.task;
+            taskId = data.taskId;
             msg.obj = data;
             mService.mUiHandler.sendMessage(msg);
         }
@@ -475,25 +471,15 @@
                 stopReportingCrashesLocked(r);
             }
             if (res == AppErrorDialog.RESTART) {
-                mService.removeProcessLocked(r, false, true, "crash");
-                if (task != null) {
+                mService.mProcessList.removeProcessLocked(r, false, true, "crash");
+                if (taskId != INVALID_TASK_ID) {
                     try {
-                        mService.mActivityTaskManager.startActivityFromRecents(task.taskId,
+                        mService.mActivityTaskManager.startActivityFromRecents(taskId,
                                 ActivityOptions.makeBasic().toBundle());
                     } catch (IllegalArgumentException e) {
-                        // Hmm, that didn't work, app might have crashed before creating a
-                        // recents entry. Let's see if we have a safe-to-restart intent.
-                        final Set<String> cats = task.intent != null
-                                ? task.intent.getCategories() : null;
-                        if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
-                            mService.mActivityTaskManager.getActivityStartController().startActivityInPackage(
-                                    task.mCallingUid, callingPid, callingUid, task.mCallingPackage,
-                                    task.intent, null, null, null, 0, 0,
-                                    new SafeActivityOptions(ActivityOptions.makeBasic()),
-                                    task.userId, null,
-                                    "AppErrors", false /*validateIncomingUser*/,
-                                    null /* originatingPendingIntent */);
-                        }
+                        // Hmm...that didn't work. Task should either be in recents or associated
+                        // with a stack.
+                        Slog.e(TAG, "Could not restart taskId=" + taskId, e);
                     }
                 }
             }
@@ -501,10 +487,10 @@
                 long orig = Binder.clearCallingIdentity();
                 try {
                     // Kill it with fire!
-                    mService.mStackSupervisor.handleAppCrashLocked(r.getWindowProcessController());
+                    mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
                     if (!r.isPersistent()) {
-                        mService.removeProcessLocked(r, false, false, "crash");
-                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                        mService.mProcessList.removeProcessLocked(r, false, false, "crash");
+                        mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(orig);
@@ -565,7 +551,7 @@
                     } else {
                         // Huh.
                         Process.killProcess(pid);
-                        ActivityManagerService.killProcessGroup(uid, pid);
+                        ProcessList.killProcessGroup(uid, pid);
                     }
                 }
                 return true;
@@ -582,27 +568,12 @@
         app.setCrashing(true);
         app.crashingReport = generateProcessError(app,
                 ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);
-        startAppProblemLocked(app);
+        app.startAppProblemLocked();
         app.getWindowProcessController().stopFreezingActivities();
         return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace,
                 data);
     }
 
-    void startAppProblemLocked(ProcessRecord app) {
-        // If this app is not running under the current user, then we
-        // can't give it a report button because that would require
-        // launching the report UI under a different user.
-        app.errorReportReceiver = null;
-
-        for (int userId : mService.mUserController.getCurrentProfileIds()) {
-            if (app.userId == userId) {
-                app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
-                        mContext, app.info.packageName, app.info.flags);
-            }
-        }
-        mService.skipCurrentReceiverLocked(app);
-    }
-
     /**
      * Generate a process error record, suitable for attachment to a ProcessRecord.
      *
@@ -616,7 +587,7 @@
      *
      * @return Returns a fully-formed ProcessErrorStateInfo record.
      */
-    private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
+    ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
             int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
         ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
 
@@ -684,7 +655,7 @@
                 Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
 
         final boolean procIsBoundForeground =
-            (app.curProcState == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+            (app.getCurProcState() == ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
 
         Long crashTime;
         Long crashTimePersistent;
@@ -723,7 +694,7 @@
                     + " has crashed too many times: killing!");
             EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
                     app.userId, app.info.processName, app.uid);
-            mService.mStackSupervisor.handleAppCrashLocked(app.getWindowProcessController());
+            mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
             if (!app.isPersistent()) {
                 // We don't want to start this process again until the user
                 // explicitly does so...  but for persistent process, we really
@@ -743,18 +714,18 @@
                 // Don't let services in this process be restarted and potentially
                 // annoy the user repeatedly.  Unless it is persistent, since those
                 // processes run critical code.
-                mService.removeProcessLocked(app, false, tryAgain, "crash");
-                mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mService.mProcessList.removeProcessLocked(app, false, tryAgain, "crash");
+                mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
                 if (!showBackground) {
                     return false;
                 }
             }
-            mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
         } else {
-            final TaskRecord affectedTask =
-                    mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app.getWindowProcessController(), reason);
+            final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
+                            app.getWindowProcessController(), reason);
             if (data != null) {
-                data.task = affectedTask;
+                data.taskId = affectedTaskId;
             }
             if (data != null && crashTimePersistent != null
                     && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
@@ -854,259 +825,13 @@
         }
     }
 
-    void stopReportingCrashesLocked(ProcessRecord proc) {
+    private void stopReportingCrashesLocked(ProcessRecord proc) {
         if (mAppsNotReportingCrashes == null) {
             mAppsNotReportingCrashes = new ArraySet<>();
         }
         mAppsNotReportingCrashes.add(proc.info.packageName);
     }
 
-    static boolean isInterestingForBackgroundTraces(ProcessRecord app) {
-        // The system_server is always considered interesting.
-        if (app.pid == MY_PID) {
-            return true;
-        }
-
-        // A package is considered interesting if any of the following is true :
-        //
-        // - It's displaying an activity.
-        // - It's the SystemUI.
-        // - It has an overlay or a top UI visible.
-        //
-        // NOTE: The check whether a given ProcessRecord belongs to the systemui
-        // process is a bit of a kludge, but the same pattern seems repeated at
-        // several places in the system server.
-        return app.isInterestingToUserLocked() ||
-            (app.info != null && "com.android.systemui".equals(app.info.packageName)) ||
-            (app.hasTopUi || app.hasOverlayUi);
-    }
-
-    final void appNotResponding(ProcessRecord app, ActivityRecord activity,
-            ActivityRecord parent, boolean aboveSystem, final String annotation) {
-        ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
-        SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);
-
-        if (mService.mActivityTaskManager.mController != null) {
-            try {
-                // 0 == continue, -1 = kill process immediately
-                int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
-                        app.processName, app.pid, annotation);
-                if (res < 0 && app.pid != MY_PID) {
-                    app.kill("anr", true);
-                }
-            } catch (RemoteException e) {
-                mService.mActivityTaskManager.mController = null;
-                Watchdog.getInstance().setActivityController(null);
-            }
-        }
-
-        long anrTime = SystemClock.uptimeMillis();
-        if (ActivityManagerService.MONITOR_CPU_USAGE) {
-            mService.updateCpuStatsNow();
-        }
-
-        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
-        boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
-
-        boolean isSilentANR;
-
-        synchronized (mService) {
-            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
-            if (mService.mActivityTaskManager.mShuttingDown) {
-                Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
-                return;
-            } else if (app.isNotResponding()) {
-                Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
-                return;
-            } else if (app.isCrashing()) {
-                Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
-                return;
-            } else if (app.killedByAm) {
-                Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);
-                return;
-            } else if (app.killed) {
-                Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation);
-                return;
-            }
-
-            // In case we come through here for the same app before completing
-            // this one, mark as anring now so we will bail out.
-            app.setNotResponding(true);
-
-            // Log the ANR to the event log.
-            EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
-                    app.processName, app.info.flags, annotation);
-
-            // Dump thread traces as quickly as we can, starting with "interesting" processes.
-            firstPids.add(app.pid);
-
-            // Don't dump other PIDs if it's a background ANR
-            isSilentANR = !showBackground && !isInterestingForBackgroundTraces(app);
-            if (!isSilentANR) {
-                int parentPid = app.pid;
-                if (parent != null && parent.app != null && parent.app.getPid() > 0) {
-                    parentPid = parent.app.getPid();
-                }
-                if (parentPid != app.pid) firstPids.add(parentPid);
-
-                if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);
-
-                for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
-                    ProcessRecord r = mService.mLruProcesses.get(i);
-                    if (r != null && r.thread != null) {
-                        int pid = r.pid;
-                        if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
-                            if (r.isPersistent()) {
-                                firstPids.add(pid);
-                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
-                            } else if (r.treatLikeActivity) {
-                                firstPids.add(pid);
-                                if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
-                            } else {
-                                lastPids.put(pid, Boolean.TRUE);
-                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // Log the ANR to the main log.
-        StringBuilder info = new StringBuilder();
-        info.setLength(0);
-        info.append("ANR in ").append(app.processName);
-        if (activity != null && activity.shortComponentName != null) {
-            info.append(" (").append(activity.shortComponentName).append(")");
-        }
-        info.append("\n");
-        info.append("PID: ").append(app.pid).append("\n");
-        if (annotation != null) {
-            info.append("Reason: ").append(annotation).append("\n");
-        }
-        if (parent != null && parent != activity) {
-            info.append("Parent: ").append(parent.shortComponentName).append("\n");
-        }
-
-        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
-
-        // don't dump native PIDs for background ANRs unless it is the process of interest
-        String[] nativeProcs = null;
-        if (isSilentANR) {
-            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
-                if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {
-                    nativeProcs = new String[] { app.processName };
-                    break;
-                }
-            }
-        } else {
-            nativeProcs = NATIVE_STACKS_OF_INTEREST;
-        }
-
-        int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
-        ArrayList<Integer> nativePids = null;
-
-        if (pids != null) {
-            nativePids = new ArrayList<Integer>(pids.length);
-            for (int i : pids) {
-                nativePids.add(i);
-            }
-        }
-
-        // For background ANRs, don't pass the ProcessCpuTracker to
-        // avoid spending 1/2 second collecting stats to rank lastPids.
-        File tracesFile = ActivityManagerService.dumpStackTraces(
-                firstPids,
-                (isSilentANR) ? null : processCpuTracker,
-                (isSilentANR) ? null : lastPids,
-                nativePids);
-
-        String cpuInfo = null;
-        if (ActivityManagerService.MONITOR_CPU_USAGE) {
-            mService.updateCpuStatsNow();
-            synchronized (mService.mProcessCpuTracker) {
-                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
-            }
-            info.append(processCpuTracker.printCurrentLoad());
-            info.append(cpuInfo);
-        }
-
-        info.append(processCpuTracker.printCurrentState(anrTime));
-
-        Slog.e(TAG, info.toString());
-        if (tracesFile == null) {
-            // There is no trace file, so dump (only) the alleged culprit's threads to the log
-            Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
-        }
-
-        StatsLog.write(StatsLog.ANR_OCCURRED, app.uid, app.processName,
-                activity == null ? "unknown": activity.shortComponentName, annotation,
-                (app.info != null) ? (app.info.isInstantApp()
-                        ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
-                        : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
-                        : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
-                app != null ? (app.isInterestingToUserLocked()
-                        ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
-                        : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND)
-                        : StatsLog.ANROCCURRED__FOREGROUND_STATE__UNKNOWN);
-        mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
-                cpuInfo, tracesFile, null);
-
-        if (mService.mActivityTaskManager.mController != null) {
-            try {
-                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
-                int res = mService.mActivityTaskManager.mController.appNotResponding(
-                        app.processName, app.pid, info.toString());
-                if (res != 0) {
-                    if (res < 0 && app.pid != MY_PID) {
-                        app.kill("anr", true);
-                    } else {
-                        synchronized (mService) {
-                            mService.mServices.scheduleServiceTimeoutLocked(app);
-                        }
-                    }
-                    return;
-                }
-            } catch (RemoteException e) {
-                mService.mActivityTaskManager.mController = null;
-                Watchdog.getInstance().setActivityController(null);
-            }
-        }
-
-        synchronized (mService) {
-            mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
-
-            if (isSilentANR) {
-                app.kill("bg anr", true);
-                return;
-            }
-
-            // Set the app's notResponding state, and look up the errorReportReceiver
-            makeAppNotRespondingLocked(app,
-                    activity != null ? activity.shortComponentName : null,
-                    annotation != null ? "ANR " + annotation : "ANR",
-                    info.toString());
-
-            // Bring up the infamous App Not Responding dialog
-            Message msg = Message.obtain();
-            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
-            msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
-
-            mService.mUiHandler.sendMessage(msg);
-        }
-    }
-
-    private void makeAppNotRespondingLocked(ProcessRecord app,
-            String activity, String shortMsg, String longMsg) {
-        app.setNotResponding(true);
-        app.notRespondingReport = generateProcessError(app,
-                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
-                activity, shortMsg, longMsg, null);
-        startAppProblemLocked(app);
-        app.getWindowProcessController().stopFreezingActivities();
-    }
-
     void handleShowAnrUi(Message msg) {
         Dialog dialogToShow = null;
         synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 7c983ff..cb76e2f 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.content.pm.ApplicationInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 
@@ -58,8 +59,8 @@
         setCancelable(false);
 
         int resid;
-        CharSequence name1 = data.activity != null
-                ? data.activity.info.loadLabel(context.getPackageManager())
+        CharSequence name1 = data.aInfo != null
+                ? data.aInfo.loadLabel(context.getPackageManager())
                 : null;
         CharSequence name2 = null;
         if ((mProc.pkgList.size() == 1) &&
@@ -181,12 +182,12 @@
 
     static class Data {
         final ProcessRecord proc;
-        final ActivityRecord activity;
+        final ApplicationInfo aInfo;
         final boolean aboveSystem;
 
-        Data(ProcessRecord proc, ActivityRecord activity, boolean aboveSystem) {
+        Data(ProcessRecord proc, ApplicationInfo aInfo, boolean aboveSystem) {
             this.proc = proc;
-            this.activity = activity;
+            this.aInfo = aInfo;
             this.aboveSystem = aboveSystem;
         }
     }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e2035f6..a0977be 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -191,7 +191,7 @@
 
         @Override
         public void run() {
-            mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
+            mApp.appNotResponding(null, null, null, null, false, mAnnotation);
         }
     }
 
@@ -286,7 +286,7 @@
         r.curApp = app;
         app.curReceivers.add(r);
         app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
-        mService.updateLruProcessLocked(app, false, null);
+        mService.mProcessList.updateLruProcessLocked(app, false, null);
         if (!skipOomAdj) {
             mService.updateOomAdjLocked();
         }
@@ -892,7 +892,7 @@
                     isDead = proc == null || proc.isCrashing();
                 }
             } else {
-                final ProcessRecord proc = mService.mProcessNames.get(
+                final ProcessRecord proc = mService.mProcessList.mProcessNames.get(
                         mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
                 isDead = proc == null || !proc.pendingStart;
             }
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 536f3a9..3c4ab00 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -16,8 +16,11 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -48,7 +51,7 @@
 import android.util.Xml;
 
 public final class CompatModePackages {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
     private final ActivityTaskManagerService mService;
@@ -61,7 +64,7 @@
 
     private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>();
 
-    private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
+    private static final int MSG_WRITE = 300;
 
     private final CompatHandler mHandler;
 
@@ -321,16 +324,16 @@
             ActivityRecord starting = stack.restartPackage(packageName);
 
             // Tell all processes that loaded this package about the change.
-            for (int i = mService.mAm.mLruProcesses.size() - 1; i >= 0; i--) {
-                final ProcessRecord app = mService.mAm.mLruProcesses.get(i);
-                if (!app.pkgList.containsKey(packageName)) {
+            for (int i = mService.mPidMap.size() - 1; i >= 0; i--) {
+                final WindowProcessController app = mService.mPidMap.valueAt(i);
+                if (!app.mPkgList.contains(packageName)) {
                     continue;
                 }
                 try {
-                    if (app.thread != null) {
+                    if (app.hasThread()) {
                         if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
-                                + app.processName + " new compat " + ci);
-                        app.thread.updatePackageCompatibilityInfo(packageName, ci);
+                                + app.mName + " new compat " + ci);
+                        app.getThread().updatePackageCompatibilityInfo(packageName, ci);
                     }
                 } catch (Exception e) {
                 }
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 09c152e..48e26ed 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -56,6 +56,10 @@
 
         sGlobalSettingToTypeMap.put(Settings.Global.DEBUG_VIEW_ATTRIBUTES, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.ANGLE_ENABLED_APP, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class);
+        sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
         // add other global settings here...
     }
 
@@ -122,6 +126,7 @@
                 value = Settings.Global.getString(context.getContentResolver(), setting);
             }
             if (value == null) {
+                snapshot.remove(setting);
                 continue;
             }
             Class<?> type = entry.getValue();
diff --git a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
index e5add58..b39873f 100644
--- a/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
+++ b/services/core/java/com/android/server/am/DeprecatedTargetSdkVersionDialog.java
@@ -16,8 +16,8 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.app.AlertDialog;
 import android.content.Context;
@@ -34,7 +34,7 @@
 import com.android.server.utils.AppInstallerUtil;
 
 public class DeprecatedTargetSdkVersionDialog {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "DeprecatedTargetSdkVersionDialog" : TAG_ATM;
 
     private final AlertDialog mDialog;
     private final String mPackageName;
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index cfe2829..28b2a42 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -30,9 +30,9 @@
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
 import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
 import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
@@ -60,7 +60,7 @@
  */
 class KeyguardController {
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_ATM;
 
     private final ActivityStackSupervisor mStackSupervisor;
     private WindowManagerService mWindowManager;
diff --git a/services/core/java/com/android/server/am/LaunchParamsController.java b/services/core/java/com/android/server/am/LaunchParamsController.java
index 218d908..68e897f 100644
--- a/services/core/java/com/android/server/am/LaunchParamsController.java
+++ b/services/core/java/com/android/server/am/LaunchParamsController.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
@@ -97,6 +98,15 @@
                     break;
             }
         }
+
+        if (activity != null && activity.requestedVrComponent != null) {
+            // Check if the Activity is a VR activity. If so, it should be launched in main display.
+            result.mPreferredDisplayId = DEFAULT_DISPLAY;
+        } else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
+            // Get the virtual display ID from ActivityTaskManagerService. If that's set we
+            // should always use that.
+            result.mPreferredDisplayId = mService.mVr2dDisplayId;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 643c922..5b31d5f 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -28,10 +28,10 @@
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -84,7 +84,7 @@
  * @see Activity#stopLockTask()
  */
 public class LockTaskController {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "LockTaskController" : TAG_ATM;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index a8e1ccc..85ee7e6 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -16,7 +16,7 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -37,6 +37,38 @@
  * Static utility methods related to {@link MemoryStat}.
  */
 final class MemoryStatUtil {
+    /**
+     * Which native processes to create {@link MemoryStat} for.
+     *
+     * <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
+     * /system/bin/statsd for the stats daemon.
+     */
+    static final String[] MEMORY_STAT_INTERESTING_NATIVE_PROCESSES = new String[]{
+            "/system/bin/statsd",  // Stats daemon.
+            "/system/bin/surfaceflinger",
+            "/system/bin/apexd",  // APEX daemon.
+            "/system/bin/audioserver",
+            "/system/bin/cameraserver",
+            "/system/bin/drmserver",
+            "/system/bin/healthd",
+            "/system/bin/incidentd",
+            "/system/bin/installd",
+            "/system/bin/lmkd",  // Low memory killer daemon.
+            "/system/bin/logd",
+            "media.codec",
+            "media.extractor",
+            "media.metrics",
+            "/system/bin/mediadrmserver",
+            "/system/bin/mediaserver",
+            "/system/bin/performanced",
+            "/system/bin/tombstoned",
+            "/system/bin/traced",  // Perfetto.
+            "/system/bin/traced_probes",  // Perfetto.
+            "webview_zygote",
+            "zygote",
+            "zygote64",
+    };
+
     static final int BYTES_IN_KILOBYTE = 1024;
     static final int PAGE_SIZE = 4096;
 
@@ -57,6 +89,8 @@
     private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
     /** Path to procfs status file for logging app memory state */
     private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
+    /** Path to procfs cmdline file. Used with pid: /proc/pid/cmdline. */
+    private static final String PROC_CMDLINE_FILE_FMT = "/proc/%d/cmdline";
 
     private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
     private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -119,6 +153,18 @@
         return stat;
     }
 
+    /**
+     * Reads cmdline of a process from procfs.
+     *
+     * Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
+     * if the file is not available.
+     */
+    static String readCmdlineFromProcfs(int pid) {
+        String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
+        String cmdline = readFileContents(path);
+        return cmdline != null ? cmdline : "";
+    }
+
     private static String readFileContents(String path) {
         final File file = new File(path);
         if (!file.exists()) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index b9c6fa6..2dcddff 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -288,12 +288,19 @@
                 resolvedType = key.requestResolvedType;
             }
 
+            // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
+            // can specify a consistent launch mode even if the PendingIntent is immutable
+            final ActivityOptions opts = ActivityOptions.fromBundle(options);
+            if (opts != null) {
+                finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+            }
+
             // Extract options before clearing calling identity
             mergedOptions = key.options;
             if (mergedOptions == null) {
-                mergedOptions = SafeActivityOptions.fromBundle(options);
+                mergedOptions = new SafeActivityOptions(opts);
             } else {
-                mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+                mergedOptions.setCallerOptions(opts);
             }
 
             if (whitelistDuration != null) {
diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java
new file mode 100644
index 0000000..b36e3c7
--- /dev/null
+++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import android.util.SparseArray;
+
+/** Whitelists of uids to temporarily bypass Power Save mode. */
+final class PendingTempWhitelists {
+
+    private ActivityManagerService mService;
+
+    private final SparseArray<ActivityManagerService.PendingTempWhitelist> mPendingTempWhitelist =
+            new SparseArray<>();
+
+    PendingTempWhitelists(ActivityManagerService service) {
+        mService = service;
+    }
+
+    void put(int uid, ActivityManagerService.PendingTempWhitelist value) {
+        mPendingTempWhitelist.put(uid, value);
+        mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag);
+    }
+
+    void removeAt(int index) {
+        final int uid = mPendingTempWhitelist.keyAt(index);
+        mPendingTempWhitelist.removeAt(index);
+        mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid);
+    }
+
+    ActivityManagerService.PendingTempWhitelist get(int uid) {
+        return mPendingTempWhitelist.get(uid);
+    }
+
+    int size() {
+        return mPendingTempWhitelist.size();
+    }
+
+    ActivityManagerService.PendingTempWhitelist valueAt(int index) {
+        return mPendingTempWhitelist.valueAt(index);
+    }
+
+    int indexOfKey(int key) {
+        return mPendingTempWhitelist.indexOfKey(key);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 9e7ce32..805b979b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -16,28 +16,94 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.os.Process.FIRST_ISOLATED_UID;
+import static android.os.Process.LAST_ISOLATED_UID;
+import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.getFreeMemory;
+import static android.os.Process.getTotalMemory;
+import static android.os.Process.killProcessQuiet;
+import static android.os.Process.startWebView;
+import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_MSG;
+import static com.android.server.am.ActivityManagerService.PROC_START_TIMEOUT_WITH_WRAPPER;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.TAG_LRU;
+import static com.android.server.am.ActivityManagerService.TAG_PROCESSES;
+import static com.android.server.am.ActivityManagerService.TAG_PSS;
+import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS;
 
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.AppProtoEnums;
+import android.app.IApplicationThread;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.StatsLog;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.Zygote;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.MemInfoReader;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.Watchdog;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.nio.ByteBuffer;
-
-import android.app.ActivityManager;
-import android.app.AppProtoEnums;
-import android.os.Build;
-import android.os.SystemClock;
-import com.android.internal.util.MemInfoReader;
-import com.android.server.wm.WindowManagerService;
-
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.os.SystemProperties;
-import android.net.LocalSocketAddress;
-import android.net.LocalSocket;
-import android.util.Slog;
-import android.view.Display;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Activity manager code dealing with processes.
@@ -47,7 +113,7 @@
 
     // The minimum time we allow between crashes, for us to consider this
     // application to be bad and stop and its services and reject broadcasts.
-    static final int MIN_CRASH_INTERVAL = 60*1000;
+    static final int MIN_CRASH_INTERVAL = 60 * 1000;
 
     // OOM adjustments for processes in various states:
 
@@ -129,7 +195,7 @@
     static final int NATIVE_ADJ = -1000;
 
     // Memory pages are 4K.
-    static final int PAGE_SIZE = 4*1024;
+    static final int PAGE_SIZE = 4 * 1024;
 
     // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
     static final int SCHED_GROUP_BACKGROUND = 0;
@@ -148,7 +214,7 @@
     static final int MIN_CACHED_APPS = 2;
 
     // We allow empty processes to stick around for at most 30 minutes.
-    static final long MAX_EMPTY_TIME = 30*60*1000;
+    static final long MAX_EMPTY_TIME = 30 * 60 * 1000;
 
     // Threshold of number of cached+empty where we consider memory critical.
     static final int TRIM_CRITICAL_THRESHOLD = 3;
@@ -168,6 +234,12 @@
     static final byte LMK_PROCREMOVE = 2;
     static final byte LMK_PROCPURGE = 3;
 
+    ActivityManagerService mService = null;
+
+    // To kill process groups asynchronously
+    static KillHandler sKillHandler = null;
+    static ServiceThread sKillThread = null;
+
     // These are the various interesting memory levels that we will give to
     // the OOM killer.  Note that the OOM killer only supports 6 slots, so we
     // can't give it a different value for every possible kind of process.
@@ -199,6 +271,123 @@
     private static LocalSocket sLmkdSocket;
     private static OutputStream sLmkdOutputStream;
 
+    /**
+     * Temporary to avoid allocations.  Protected by main lock.
+     */
+    @GuardedBy("mService")
+    final StringBuilder mStringBuilder = new StringBuilder(256);
+
+    /**
+     * A global counter for generating sequence numbers.
+     * This value will be used when incrementing sequence numbers in individual uidRecords.
+     *
+     * Having a global counter ensures that seq numbers are monotonically increasing for a
+     * particular uid even when the uidRecord is re-created.
+     */
+    @GuardedBy("mService")
+    @VisibleForTesting
+    long mProcStateSeqCounter = 0;
+
+    /**
+     * A global counter for generating sequence numbers to uniquely identify pending process starts.
+     */
+    @GuardedBy("mService")
+    private long mProcStartSeqCounter = 0;
+
+    /**
+     * Contains {@link ProcessRecord} objects for pending process starts.
+     *
+     * Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
+     */
+    @GuardedBy("mService")
+    final LongSparseArray<ProcessRecord> mPendingStarts = new LongSparseArray<>();
+
+    /**
+     * List of running applications, sorted by recent usage.
+     * The first entry in the list is the least recently used.
+     */
+    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
+
+    /**
+     * Where in mLruProcesses that the processes hosting activities start.
+     */
+    int mLruProcessActivityStart = 0;
+
+    /**
+     * Where in mLruProcesses that the processes hosting services start.
+     * This is after (lower index) than mLruProcessesActivityStart.
+     */
+    int mLruProcessServiceStart = 0;
+
+    /**
+     * Current sequence id for process LRU updating.
+     */
+    int mLruSeq = 0;
+
+    /**
+     * The currently running isolated processes.
+     */
+    final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<>();
+
+    /**
+     * Counter for assigning isolated process uids, to avoid frequently reusing the
+     * same ones.
+     */
+    int mNextIsolatedProcessUid = 0;
+
+    /**
+     * Processes that are being forcibly torn down.
+     */
+    final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
+
+    /**
+     * All of the applications we currently have running organized by name.
+     * The keys are strings of the application package name (as
+     * returned by the package manager), and the keys are ApplicationRecord
+     * objects.
+     */
+    final MyProcessMap mProcessNames = new MyProcessMap();
+
+    final class MyProcessMap extends ProcessMap<ProcessRecord> {
+        @Override
+        public ProcessRecord put(String name, int uid, ProcessRecord value) {
+            final ProcessRecord r = super.put(name, uid, value);
+            mService.mAtmInternal.onProcessAdded(r.getWindowProcessController());
+            return r;
+        }
+
+        @Override
+        public ProcessRecord remove(String name, int uid) {
+            final ProcessRecord r = super.remove(name, uid);
+            mService.mAtmInternal.onProcessRemoved(name, uid);
+            return r;
+        }
+    }
+
+    final class KillHandler extends Handler {
+        static final int KILL_PROCESS_GROUP_MSG = 4000;
+
+        public KillHandler(Looper looper) {
+            super(looper, null, true);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case KILL_PROCESS_GROUP_MSG:
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
+                    Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                    break;
+
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    ////////////////////  END FIELDS  ////////////////////
+
     ProcessList() {
         MemInfoReader minfo = new MemInfoReader();
         minfo.readMemInfo();
@@ -206,6 +395,16 @@
         updateOomLevels(0, 0, false);
     }
 
+    void init(ActivityManagerService service) {
+        mService = service;
+        if (sKillHandler == null) {
+            sKillThread = new ServiceThread(TAG + ":kill",
+                    THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+            sKillThread.start();
+            sKillHandler = new KillHandler(sKillThread.getLooper());
+        }
+    }
+
     void applyDisplaySize(WindowManagerService wm) {
         if (!mHaveDisplaySize) {
             Point p = new Point();
@@ -221,12 +420,12 @@
     private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
         // Scale buckets from avail memory: at 300MB we use the lowest values to
         // 700MB or more for the top values.
-        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
+        float scaleMem = ((float) (mTotalMemMb - 350)) / (700 - 350);
 
         // Scale buckets from screen size.
-        int minSize = 480*800;  //  384000
-        int maxSize = 1280*800; // 1024000  230400 870400  .264
-        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
+        int minSize = 480 * 800;  //  384000
+        int maxSize = 1280 * 800; // 1024000  230400 870400  .264
+        float scaleDisp = ((float)(displayWidth * displayHeight) - minSize) / (maxSize - minSize);
         if (false) {
             Slog.i("XXXXXX", "scaleMem=" + scaleMem);
             Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
@@ -246,27 +445,27 @@
 
         final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
 
-        for (int i=0; i<mOomAdj.length; i++) {
+        for (int i = 0; i < mOomAdj.length; i++) {
             int low = mOomMinFreeLow[i];
             int high = mOomMinFreeHigh[i];
             if (is64bit) {
                 // Increase the high min-free levels for cached processes for 64-bit
-                if (i == 4) high = (high*3)/2;
-                else if (i == 5) high = (high*7)/4;
+                if (i == 4) high = (high * 3) / 2;
+                else if (i == 5) high = (high * 7) / 4;
             }
-            mOomMinFree[i] = (int)(low + ((high-low)*scale));
+            mOomMinFree[i] = (int)(low + ((high - low) * scale));
         }
 
         if (minfree_abs >= 0) {
-            for (int i=0; i<mOomAdj.length; i++) {
+            for (int i = 0; i < mOomAdj.length; i++) {
                 mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
                         / mOomMinFree[mOomAdj.length - 1]);
             }
         }
 
         if (minfree_adj != 0) {
-            for (int i=0; i<mOomAdj.length; i++) {
-                mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
+            for (int i = 0; i < mOomAdj.length; i++) {
+                mOomMinFree[i] += (int)((float) minfree_adj * mOomMinFree[i]
                         / mOomMinFree[mOomAdj.length - 1]);
                 if (mOomMinFree[i] < 0) {
                     mOomMinFree[i] = 0;
@@ -277,13 +476,15 @@
         // The maximum size we will restore a process from cached to background, when under
         // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
         // before killing background processes.
-        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;
+        mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024) / 3;
 
         // Ask the kernel to try to keep enough memory free to allocate 3 full
         // screen 32bpp buffers without entering direct reclaim.
         int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
-        int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
-        int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
+        int reserve_adj = Resources.getSystem().getInteger(
+                com.android.internal.R.integer.config_extraFreeKbytesAdjust);
+        int reserve_abs = Resources.getSystem().getInteger(
+                com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
 
         if (reserve_abs >= 0) {
             reserve = reserve_abs;
@@ -297,10 +498,10 @@
         }
 
         if (write) {
-            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
+            ByteBuffer buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
             buf.putInt(LMK_TARGET);
-            for (int i=0; i<mOomAdj.length; i++) {
-                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
+            for (int i = 0; i < mOomAdj.length; i++) {
+                buf.putInt((mOomMinFree[i] * 1024)/PAGE_SIZE);
                 buf.putInt(mOomAdj[i]);
             }
 
@@ -320,7 +521,7 @@
             if (space == null) return prefix;
             return prefix + "  ";
         }
-        return prefix + "+" + Integer.toString(val-base);
+        return prefix + "+" + Integer.toString(val - base);
     }
 
     public static String makeOomAdjString(int setAdj) {
@@ -477,7 +678,7 @@
     }
 
     public static void appendRamKb(StringBuilder sb, long ramKb) {
-        for (int j=0, fact=10; j<6; j++, fact*=10) {
+        for (int j = 0, fact = 10; j < 6; j++, fact *= 10) {
             if (ramKb < fact) {
                 sb.append(' ');
             }
@@ -737,12 +938,12 @@
     }
 
     long getMemLevel(int adjustment) {
-        for (int i=0; i<mOomAdj.length; i++) {
+        for (int i = 0; i < mOomAdj.length; i++) {
             if (adjustment <= mOomAdj[i]) {
                 return mOomMinFree[i] * 1024;
             }
         }
-        return mOomMinFree[mOomAdj.length-1] * 1024;
+        return mOomMinFree[mOomAdj.length - 1] * 1024;
     }
 
     /**
@@ -858,4 +1059,1521 @@
             }
         }
     }
+
+    static void killProcessGroup(int uid, int pid) {
+        /* static; one-time init here */
+        if (sKillHandler != null) {
+            sKillHandler.sendMessage(
+                    sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
+        } else {
+            Slog.w(TAG, "Asked to kill process group before system bringup!");
+            Process.killProcessGroup(uid, pid);
+        }
+    }
+
+    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean
+            keepIfLarge) {
+        if (uid == SYSTEM_UID) {
+            // The system gets to run in any process.  If there are multiple
+            // processes with the same uid, just pick the first (this
+            // should never happen).
+            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
+            if (procs == null) return null;
+            final int procCount = procs.size();
+            for (int i = 0; i < procCount; i++) {
+                final int procUid = procs.keyAt(i);
+                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
+                    // Don't use an app process or different user process for system component.
+                    continue;
+                }
+                return procs.valueAt(i);
+            }
+        }
+        ProcessRecord proc = mProcessNames.get(processName, uid);
+        if (false && proc != null && !keepIfLarge
+                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                && proc.lastCachedPss >= 4000) {
+            // Turn this condition on to cause killing to happen regularly, for testing.
+            if (proc.baseProcessTracker != null) {
+                proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss);
+                for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                    StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+                            proc.info.uid,
+                            holder.state.getName(),
+                            holder.state.getPackage(),
+                            proc.lastCachedPss, holder.appVersion);
+                }
+            }
+            proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
+        } else if (proc != null && !keepIfLarge
+                && mService.mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc
+                    .lastCachedPss);
+            if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) {
+                if (proc.baseProcessTracker != null) {
+                    proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList,
+                            proc.lastCachedPss);
+                    for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
+                        StatsLog.write(StatsLog.CACHED_KILL_REPORTED,
+                                proc.info.uid,
+                                holder.state.getName(),
+                                holder.state.getPackage(),
+                                proc.lastCachedPss, holder.appVersion);
+                    }
+                }
+                proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
+            }
+        }
+        return proc;
+    }
+
+    void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
+        final long homeAppMem = getMemLevel(HOME_APP_ADJ);
+        final long cachedAppMem = getMemLevel(CACHED_APP_MIN_ADJ);
+        outInfo.availMem = getFreeMemory();
+        outInfo.totalMem = getTotalMemory();
+        outInfo.threshold = homeAppMem;
+        outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
+        outInfo.hiddenAppThreshold = cachedAppMem;
+        outInfo.secondaryServerThreshold = getMemLevel(SERVICE_ADJ);
+        outInfo.visibleAppThreshold = getMemLevel(VISIBLE_APP_ADJ);
+        outInfo.foregroundAppThreshold = getMemLevel(FOREGROUND_APP_ADJ);
+    }
+
+    ProcessRecord findAppProcessLocked(IBinder app, String reason) {
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                ProcessRecord p = apps.valueAt(ia);
+                if (p.thread != null && p.thread.asBinder() == app) {
+                    return p;
+                }
+            }
+        }
+
+        Slog.w(TAG, "Can't find mystery application for " + reason
+                + " from pid=" + Binder.getCallingPid()
+                + " uid=" + Binder.getCallingUid() + ": " + app);
+        return null;
+    }
+
+    private void checkSlow(long startTime, String where) {
+        long now = SystemClock.uptimeMillis();
+        if ((now - startTime) > 50) {
+            // If we are taking more than 50ms, log about it.
+            Slog.w(TAG, "Slow operation: " + (now - startTime) + "ms so far, now at " + where);
+        }
+    }
+
+    /**
+     * @return {@code true} if process start is successful, false otherwise.
+     * @param app
+     * @param hostingType
+     * @param hostingNameStr
+     * @param disableHiddenApiChecks
+     * @param abiOverride
+     */
+    @GuardedBy("mService")
+    boolean startProcessLocked(ProcessRecord app, String hostingType,
+            String hostingNameStr, boolean disableHiddenApiChecks, boolean mountExtStorageFull,
+            String abiOverride) {
+        if (app.pendingStart) {
+            return true;
+        }
+        long startTime = SystemClock.elapsedRealtime();
+        if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
+            checkSlow(startTime, "startProcess: removing from pids map");
+            synchronized (mService.mPidsSelfLocked) {
+                mService.mPidsSelfLocked.remove(app.pid);
+                mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+            }
+            checkSlow(startTime, "startProcess: done removing from pids map");
+            app.setPid(0);
+        }
+
+        if (DEBUG_PROCESSES && mService.mProcessesOnHold.contains(app)) Slog.v(
+                TAG_PROCESSES,
+                "startProcessLocked removing on hold: " + app);
+        mService.mProcessesOnHold.remove(app);
+
+        checkSlow(startTime, "startProcess: starting to update cpu stats");
+        mService.updateCpuStats();
+        checkSlow(startTime, "startProcess: done updating cpu stats");
+
+        try {
+            try {
+                final int userId = UserHandle.getUserId(app.uid);
+                AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+
+            int uid = app.uid;
+            int[] gids = null;
+            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+            if (!app.isolated) {
+                int[] permGids = null;
+                try {
+                    checkSlow(startTime, "startProcess: getting gids from package manager");
+                    final IPackageManager pm = AppGlobals.getPackageManager();
+                    permGids = pm.getPackageGids(app.info.packageName,
+                            MATCH_DIRECT_BOOT_AUTO, app.userId);
+                    if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)
+                            && mountExtStorageFull) {
+                        mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
+                    } else {
+                        StorageManagerInternal storageManagerInternal = LocalServices.getService(
+                                StorageManagerInternal.class);
+                        mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
+                                app.info.packageName);
+                    }
+                } catch (RemoteException e) {
+                    throw e.rethrowAsRuntimeException();
+                }
+
+                /*
+                 * Add shared application and profile GIDs so applications can share some
+                 * resources like shared libraries and access user-wide resources
+                 */
+                if (ArrayUtils.isEmpty(permGids)) {
+                    gids = new int[3];
+                } else {
+                    gids = new int[permGids.length + 3];
+                    System.arraycopy(permGids, 0, gids, 3, permGids.length);
+                }
+                gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
+                gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+                gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+
+                // Replace any invalid GIDs
+                if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
+                if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
+            }
+            checkSlow(startTime, "startProcess: building args");
+            if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
+                uid = 0;
+            }
+            int runtimeFlags = 0;
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+                runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
+                // Also turn on CheckJNI for debuggable apps. It's quite
+                // awkward to turn on otherwise.
+                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+            }
+            // Run the app in safe mode if its manifest requests so or the
+            // system is booted in safe mode.
+            if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
+                    mService.mSafeMode == true) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
+            }
+            if ("1".equals(SystemProperties.get("debug.checkjni"))) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+            }
+            String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
+            if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
+                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
+            }
+            String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+            if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+                runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+            }
+            if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
+            }
+            if ("1".equals(SystemProperties.get("debug.assert"))) {
+                runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
+            }
+            if (mService.mNativeDebuggingApp != null
+                    && mService.mNativeDebuggingApp.equals(app.processName)) {
+                // Enable all debug flags required by the native debugger.
+                runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
+                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
+                runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
+                mService.mNativeDebuggingApp = null;
+            }
+
+            if (app.info.isPrivilegedApp() &&
+                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
+                runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
+            }
+
+            if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
+                app.info.maybeUpdateHiddenApiEnforcementPolicy(
+                        mService.mHiddenApiBlacklist.getPolicyForPrePApps(),
+                        mService.mHiddenApiBlacklist.getPolicyForPApps());
+                @ApplicationInfo.HiddenApiEnforcementPolicy int policy =
+                        app.info.getHiddenApiEnforcementPolicy();
+                int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
+                if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
+                    throw new IllegalStateException("Invalid API policy: " + policy);
+                }
+                runtimeFlags |= policyBits;
+            }
+
+            String invokeWith = null;
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                // Debuggable apps may include a wrapper script with their library directory.
+                String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
+                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+                try {
+                    if (new File(wrapperFileName).exists()) {
+                        invokeWith = "/system/bin/logwrapper " + wrapperFileName;
+                    }
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
+                }
+            }
+
+            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
+            if (requiredAbi == null) {
+                requiredAbi = Build.SUPPORTED_ABIS[0];
+            }
+
+            String instructionSet = null;
+            if (app.info.primaryCpuAbi != null) {
+                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
+            }
+
+            app.gids = gids;
+            app.setRequiredAbi(requiredAbi);
+            app.instructionSet = instructionSet;
+
+            // the per-user SELinux context must be set
+            if (TextUtils.isEmpty(app.info.seInfoUser)) {
+                Slog.wtf(ActivityManagerService.TAG, "SELinux tag not defined",
+                        new IllegalStateException("SELinux tag not defined for "
+                                + app.info.packageName + " (uid " + app.uid + ")"));
+            }
+            final String seInfo = app.info.seInfo
+                    + (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
+            // Start the process.  It will either succeed and return a result containing
+            // the PID of the new process, or else throw a RuntimeException.
+            final String entryPoint = "android.app.ActivityThread";
+
+            return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
+                    runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
+                    startTime);
+        } catch (RuntimeException e) {
+            Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);
+
+            // Something went very wrong while trying to start this process; one
+            // common case is when the package is frozen due to an active
+            // upgrade. To recover, clean up any active bookkeeping related to
+            // starting this process. (We already invoked this method once when
+            // the package was initially frozen through KILL_APPLICATION_MSG, so
+            // it doesn't hurt to use it again.)
+            mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+                    false, false, true, false, false, app.userId, "start failure");
+            return false;
+        }
+    }
+
+    @GuardedBy("mService")
+    boolean startProcessLocked(String hostingType, String hostingNameStr,
+            String entryPoint,
+            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+            long startTime) {
+        app.pendingStart = true;
+        app.killedByAm = false;
+        app.removed = false;
+        app.killed = false;
+        final long startSeq = app.startSeq = ++mProcStartSeqCounter;
+        app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
+        if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
+            if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
+                    "Posting procStart msg for " + app.toShortString());
+            mService.mProcStartHandler.post(() -> {
+                try {
+                    synchronized (mService) {
+                        final String reason = isProcStartValidLocked(app, startSeq);
+                        if (reason != null) {
+                            Slog.w(TAG_PROCESSES, app + " not valid anymore,"
+                                    + " don't start process, " + reason);
+                            app.pendingStart = false;
+                            return;
+                        }
+                        app.setUsingWrapper(invokeWith != null
+                                || SystemProperties.get("wrap." + app.processName) != null);
+                        mPendingStarts.put(startSeq, app);
+                    }
+                    final Process.ProcessStartResult startResult = startProcess(app.hostingType,
+                            entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
+                            app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
+                    synchronized (mService) {
+                        handleProcessStartedLocked(app, startResult, startSeq);
+                    }
+                } catch (RuntimeException e) {
+                    synchronized (mService) {
+                        Slog.e(ActivityManagerService.TAG, "Failure starting process "
+                                + app.processName, e);
+                        mPendingStarts.remove(startSeq);
+                        app.pendingStart = false;
+                        mService.forceStopPackageLocked(app.info.packageName,
+                                UserHandle.getAppId(app.uid),
+                                false, false, true, false, false, app.userId, "start failure");
+                    }
+                }
+            });
+            return true;
+        } else {
+            try {
+                final Process.ProcessStartResult startResult = startProcess(hostingType,
+                        entryPoint, app,
+                        uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
+                        invokeWith, startTime);
+                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
+                        startSeq, false);
+            } catch (RuntimeException e) {
+                Slog.e(ActivityManagerService.TAG, "Failure starting process "
+                        + app.processName, e);
+                app.pendingStart = false;
+                mService.forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
+                        false, false, true, false, false, app.userId, "start failure");
+            }
+            return app.pid > 0;
+        }
+    }
+
+    private Process.ProcessStartResult startProcess(String hostingType, String entryPoint,
+            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
+            long startTime) {
+        try {
+            final String[] packageNames = mService.mContext.getPackageManager()
+                    .getPackagesForUid(uid);
+            final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
+                    .getVisibleVolumesForUser(UserHandle.getUserId(uid));
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
+                    app.processName);
+            checkSlow(startTime, "startProcess: asking zygote to start proc");
+            final Process.ProcessStartResult startResult;
+            if (hostingType.equals("webview_service")) {
+                startResult = startWebView(entryPoint,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+                        app.info.dataDir, null, app.info.packageName,
+                        packageNames, visibleVolIds,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            } else {
+                startResult = Process.start(entryPoint,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
+                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
+                        app.info.dataDir, invokeWith, app.info.packageName,
+                        packageNames, visibleVolIds,
+                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+            }
+            checkSlow(startTime, "startProcess: returned from zygote!");
+            return startResult;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    @GuardedBy("mService")
+    final void startProcessLocked(ProcessRecord app,
+            String hostingType, String hostingNameStr) {
+        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
+    }
+
+    @GuardedBy("mService")
+    final boolean startProcessLocked(ProcessRecord app,
+            String hostingType, String hostingNameStr, String abiOverride) {
+        return startProcessLocked(app, hostingType, hostingNameStr,
+                false /* disableHiddenApiChecks */, false /* mountExtStorageFull */, abiOverride);
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
+            boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
+            boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
+            String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
+        long startTime = SystemClock.elapsedRealtime();
+        ProcessRecord app;
+        if (!isolated) {
+            app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
+            checkSlow(startTime, "startProcess: after getProcessRecord");
+
+            if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
+                // If we are in the background, then check to see if this process
+                // is bad.  If so, we will just silently fail.
+                if (mService.mAppErrors.isBadProcessLocked(info)) {
+                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                            + "/" + info.processName);
+                    return null;
+                }
+            } else {
+                // When the user is explicitly starting a process, then clear its
+                // crash count so that we won't make it bad until they see at
+                // least one crash dialog again, and make the process good again
+                // if it had been bad.
+                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+                        + "/" + info.processName);
+                mService.mAppErrors.resetProcessCrashTimeLocked(info);
+                if (mService.mAppErrors.isBadProcessLocked(info)) {
+                    EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
+                            UserHandle.getUserId(info.uid), info.uid,
+                            info.processName);
+                    mService.mAppErrors.clearBadProcessLocked(info);
+                    if (app != null) {
+                        app.bad = false;
+                    }
+                }
+            }
+        } else {
+            // If this is an isolated process, it can't re-use an existing process.
+            app = null;
+        }
+
+        // We don't have to do anything more if:
+        // (1) There is an existing application record; and
+        // (2) The caller doesn't think it is dead, OR there is no thread
+        //     object attached to it so we know it couldn't have crashed; and
+        // (3) There is a pid assigned to it, so it is either starting or
+        //     already running.
+        if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
+                + " app=" + app + " knownToBeDead=" + knownToBeDead
+                + " thread=" + (app != null ? app.thread : null)
+                + " pid=" + (app != null ? app.pid : -1));
+        if (app != null && app.pid > 0) {
+            if ((!knownToBeDead && !app.killed) || app.thread == null) {
+                // We already have the app running, or are waiting for it to
+                // come up (we have a pid but not yet its thread), so keep it.
+                if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
+                // If this is a new package in the process, add the package to the list
+                app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
+                checkSlow(startTime, "startProcess: done, added package to proc");
+                return app;
+            }
+
+            // An application record is attached to a previous process,
+            // clean it up now.
+            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
+            checkSlow(startTime, "startProcess: bad proc running, killing");
+            ProcessList.killProcessGroup(app.uid, app.pid);
+            mService.handleAppDiedLocked(app, true, true);
+            checkSlow(startTime, "startProcess: done killing old proc");
+        }
+
+        String hostingNameStr = hostingName != null
+                ? hostingName.flattenToShortString() : null;
+
+        if (app == null) {
+            checkSlow(startTime, "startProcess: creating new process record");
+            app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
+            if (app == null) {
+                Slog.w(TAG, "Failed making new process record for "
+                        + processName + "/" + info.uid + " isolated=" + isolated);
+                return null;
+            }
+            app.crashHandler = crashHandler;
+            app.isolatedEntryPoint = entryPoint;
+            app.isolatedEntryPointArgs = entryPointArgs;
+            checkSlow(startTime, "startProcess: done creating new process record");
+        } else {
+            // If this is a new package in the process, add the package to the list
+            app.addPackage(info.packageName, info.versionCode, mService.mProcessStats);
+            checkSlow(startTime, "startProcess: added package to existing proc");
+        }
+
+        // If the system is not ready yet, then hold off on starting this
+        // process until it is.
+        if (!mService.mProcessesReady
+                && !mService.isAllowedWhileBooting(info)
+                && !allowWhileBooting) {
+            if (!mService.mProcessesOnHold.contains(app)) {
+                mService.mProcessesOnHold.add(app);
+            }
+            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
+                    "System not ready, putting on hold: " + app);
+            checkSlow(startTime, "startProcess: returning with proc on hold");
+            return app;
+        }
+
+        checkSlow(startTime, "startProcess: stepping in to startProcess");
+        final boolean success = startProcessLocked(app, hostingType, hostingNameStr,
+                abiOverride);
+        checkSlow(startTime, "startProcess: done starting proc!");
+        return success ? app : null;
+    }
+
+    @GuardedBy("mService")
+    private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
+        StringBuilder sb = null;
+        if (app.killedByAm) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("killedByAm=true;");
+        }
+        if (mProcessNames.get(app.processName, app.uid) != app) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("No entry in mProcessNames;");
+        }
+        if (!app.pendingStart) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("pendingStart=false;");
+        }
+        if (app.startSeq > expectedStartSeq) {
+            if (sb == null) sb = new StringBuilder();
+            sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
+        }
+        return sb == null ? null : sb.toString();
+    }
+
+    @GuardedBy("mService")
+    private boolean handleProcessStartedLocked(ProcessRecord pending,
+            Process.ProcessStartResult startResult, long expectedStartSeq) {
+        // Indicates that this process start has been taken care of.
+        if (mPendingStarts.get(expectedStartSeq) == null) {
+            if (pending.pid == startResult.pid) {
+                pending.setUsingWrapper(startResult.usingWrapper);
+                // TODO: Update already existing clients of usingWrapper
+            }
+            return false;
+        }
+        return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
+                expectedStartSeq, false);
+    }
+
+    @GuardedBy("mService")
+    boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
+            long expectedStartSeq, boolean procAttached) {
+        mPendingStarts.remove(expectedStartSeq);
+        final String reason = isProcStartValidLocked(app, expectedStartSeq);
+        if (reason != null) {
+            Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" +
+                    pid
+                    + ", " + reason);
+            app.pendingStart = false;
+            killProcessQuiet(pid);
+            Process.killProcessGroup(app.uid, app.pid);
+            return false;
+        }
+        mService.mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
+        checkSlow(app.startTime, "startProcess: done updating battery stats");
+
+        EventLog.writeEvent(EventLogTags.AM_PROC_START,
+                UserHandle.getUserId(app.startUid), pid, app.startUid,
+                app.processName, app.hostingType,
+                app.hostingNameStr != null ? app.hostingNameStr : "");
+
+        try {
+            AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+                    app.seInfo, app.info.sourceDir, pid);
+        } catch (RemoteException ex) {
+            // Ignore
+        }
+
+        if (app.isPersistent()) {
+            Watchdog.getInstance().processStarted(app.processName, pid);
+        }
+
+        checkSlow(app.startTime, "startProcess: building log message");
+        StringBuilder buf = mStringBuilder;
+        buf.setLength(0);
+        buf.append("Start proc ");
+        buf.append(pid);
+        buf.append(':');
+        buf.append(app.processName);
+        buf.append('/');
+        UserHandle.formatUid(buf, app.startUid);
+        if (app.isolatedEntryPoint != null) {
+            buf.append(" [");
+            buf.append(app.isolatedEntryPoint);
+            buf.append("]");
+        }
+        buf.append(" for ");
+        buf.append(app.hostingType);
+        if (app.hostingNameStr != null) {
+            buf.append(" ");
+            buf.append(app.hostingNameStr);
+        }
+        mService.reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
+        app.setPid(pid);
+        app.setUsingWrapper(usingWrapper);
+        app.pendingStart = false;
+        checkSlow(app.startTime, "startProcess: starting to update pids map");
+        ProcessRecord oldApp;
+        synchronized (mService.mPidsSelfLocked) {
+            oldApp = mService.mPidsSelfLocked.get(pid);
+        }
+        // If there is already an app occupying that pid that hasn't been cleaned up
+        if (oldApp != null && !app.isolated) {
+            // Clean up anything relating to this pid first
+            Slog.w(TAG, "Reusing pid " + pid
+                    + " while app is still mapped to it");
+            mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
+                    true /*replacingPid*/);
+        }
+        synchronized (mService.mPidsSelfLocked) {
+            mService.mPidsSelfLocked.put(pid, app);
+            if (!procAttached) {
+                Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+                msg.obj = app;
+                mService.mHandler.sendMessageDelayed(msg, usingWrapper
+                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
+            }
+        }
+        checkSlow(app.startTime, "startProcess: done updating pids map");
+        return true;
+    }
+
+    final void removeLruProcessLocked(ProcessRecord app) {
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui >= 0) {
+            if (!app.killed) {
+                if (app.isPersistent()) {
+                    Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
+                } else {
+                    Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
+                    if (app.pid > 0) {
+                        killProcessQuiet(app.pid);
+                        ProcessList.killProcessGroup(app.uid, app.pid);
+                    } else {
+                        app.pendingStart = false;
+                    }
+                }
+            }
+            if (lrui <= mLruProcessActivityStart) {
+                mLruProcessActivityStart--;
+            }
+            if (lrui <= mLruProcessServiceStart) {
+                mLruProcessServiceStart--;
+            }
+            mLruProcesses.remove(lrui);
+        }
+    }
+
+    void killAllBackgroundProcessesLocked() {
+        final ArrayList<ProcessRecord> procs = new ArrayList<>();
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                final ProcessRecord app = apps.valueAt(ia);
+                if (app.isPersistent()) {
+                    // We don't kill persistent processes.
+                    continue;
+                }
+                if (app.removed) {
+                    procs.add(app);
+                } else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+                    app.removed = true;
+                    procs.add(app);
+                }
+            }
+        }
+
+        final int N = procs.size();
+        for (int i = 0; i < N; i++) {
+            removeProcessLocked(procs.get(i), false, true, "kill all background");
+        }
+    }
+
+    @GuardedBy("mService")
+    final boolean killPackageProcessesLocked(String packageName, int appId,
+            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
+            boolean doit, boolean evenPersistent, String reason) {
+        ArrayList<ProcessRecord> procs = new ArrayList<>();
+
+        // Remove all processes this package may have touched: all with the
+        // same UID (except for the system or root user), and all whose name
+        // matches the package name.
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                ProcessRecord app = apps.valueAt(ia);
+                if (app.isPersistent() && !evenPersistent) {
+                    // we don't kill persistent processes
+                    continue;
+                }
+                if (app.removed) {
+                    if (doit) {
+                        procs.add(app);
+                    }
+                    continue;
+                }
+
+                // Skip process if it doesn't meet our oom adj requirement.
+                if (app.setAdj < minOomAdj) {
+                    continue;
+                }
+
+                // If no package is specified, we call all processes under the
+                // give user id.
+                if (packageName == null) {
+                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                        continue;
+                    }
+                    if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) {
+                        continue;
+                    }
+                    // Package has been specified, we want to hit all processes
+                    // that match it.  We need to qualify this by the processes
+                    // that are running under the specified app and user ID.
+                } else {
+                    final boolean isDep = app.pkgDeps != null
+                            && app.pkgDeps.contains(packageName);
+                    if (!isDep && UserHandle.getAppId(app.uid) != appId) {
+                        continue;
+                    }
+                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                        continue;
+                    }
+                    if (!app.pkgList.containsKey(packageName) && !isDep) {
+                        continue;
+                    }
+                }
+
+                // Process has passed all conditions, kill it!
+                if (!doit) {
+                    return true;
+                }
+                app.removed = true;
+                procs.add(app);
+            }
+        }
+
+        int N = procs.size();
+        for (int i=0; i<N; i++) {
+            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
+        }
+        mService.updateOomAdjLocked();
+        return N > 0;
+    }
+    @GuardedBy("mService")
+    boolean removeProcessLocked(ProcessRecord app,
+            boolean callerWillRestart, boolean allowRestart, String reason) {
+        final String name = app.processName;
+        final int uid = app.uid;
+        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
+                "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
+
+        ProcessRecord old = mProcessNames.get(name, uid);
+        if (old != app) {
+            // This process is no longer active, so nothing to do.
+            Slog.w(TAG, "Ignoring remove of inactive process: " + app);
+            return false;
+        }
+        removeProcessNameLocked(name, uid);
+        mService.mAtmInternal.clearHeavyWeightProcessIfEquals(app.getWindowProcessController());
+
+        boolean needRestart = false;
+        if ((app.pid > 0 && app.pid != ActivityManagerService.MY_PID) || (app.pid == 0 && app
+                .pendingStart)) {
+            int pid = app.pid;
+            if (pid > 0) {
+                synchronized (mService.mPidsSelfLocked) {
+                    mService.mPidsSelfLocked.remove(pid);
+                    mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
+                }
+                mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
+                if (app.isolated) {
+                    mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+                    mService.getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
+                }
+            }
+            boolean willRestart = false;
+            if (app.isPersistent() && !app.isolated) {
+                if (!callerWillRestart) {
+                    willRestart = true;
+                } else {
+                    needRestart = true;
+                }
+            }
+            app.kill(reason, true);
+            mService.handleAppDiedLocked(app, willRestart, allowRestart);
+            if (willRestart) {
+                removeLruProcessLocked(app);
+                mService.addAppLocked(app.info, null, false, null /* ABI override */);
+            }
+        } else {
+            mRemovedProcesses.add(app);
+        }
+
+        return needRestart;
+    }
+
+    @GuardedBy("mService")
+    final void addProcessNameLocked(ProcessRecord proc) {
+        // We shouldn't already have a process under this name, but just in case we
+        // need to clean up whatever may be there now.
+        ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
+        if (old == proc && proc.isPersistent()) {
+            // We are re-adding a persistent process.  Whatevs!  Just leave it there.
+            Slog.w(TAG, "Re-adding persistent process " + proc);
+        } else if (old != null) {
+            Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+        }
+        UidRecord uidRec = mService.mActiveUids.get(proc.uid);
+        if (uidRec == null) {
+            uidRec = new UidRecord(proc.uid, mService.mAtmInternal);
+            // This is the first appearance of the uid, report it now!
+            if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+                    "Creating new process uid: " + uidRec);
+            if (Arrays.binarySearch(mService.mDeviceIdleTempWhitelist,
+                    UserHandle.getAppId(proc.uid)) >= 0
+                    || mService.mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
+                uidRec.setWhitelist = uidRec.curWhitelist = true;
+            }
+            uidRec.updateHasInternetPermission();
+            mService.mActiveUids.put(proc.uid, uidRec);
+            EventLogTags.writeAmUidRunning(uidRec.uid);
+            mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState());
+        }
+        proc.uidRecord = uidRec;
+
+        // Reset render thread tid if it was already set, so new process can set it again.
+        proc.renderThreadTid = 0;
+        uidRec.numProcs++;
+        mProcessNames.put(proc.processName, proc.uid, proc);
+        if (proc.isolated) {
+            mIsolatedProcesses.put(proc.uid, proc);
+        }
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
+            boolean isolated, int isolatedUid) {
+        String proc = customProcess != null ? customProcess : info.processName;
+        final int userId = UserHandle.getUserId(info.uid);
+        int uid = info.uid;
+        if (isolated) {
+            if (isolatedUid == 0) {
+                int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
+                while (true) {
+                    if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
+                            || mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
+                        mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
+                    }
+                    uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
+                    mNextIsolatedProcessUid++;
+                    if (mIsolatedProcesses.indexOfKey(uid) < 0) {
+                        // No process for this uid, use it.
+                        break;
+                    }
+                    stepsLeft--;
+                    if (stepsLeft <= 0) {
+                        return null;
+                    }
+                }
+            } else {
+                // Special case for startIsolatedProcess (internal only), where
+                // the uid of the isolated process is specified by the caller.
+                uid = isolatedUid;
+            }
+            mService.getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
+
+            // Register the isolated UID with this application so BatteryStats knows to
+            // attribute resource usage to the application.
+            //
+            // NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
+            // about the process state of the isolated UID *before* it is registered with the
+            // owning application.
+            mService.mBatteryStatsService.addIsolatedUid(uid, info.uid);
+            StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid,
+                    StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
+        }
+        final ProcessRecord r = new ProcessRecord(mService, info, proc, uid,
+                mService.getGlobalConfiguration());
+
+        if (!mService.mBooted && !mService.mBooting
+                && userId == UserHandle.USER_SYSTEM
+                && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+            // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+            r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+            r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+            r.setPersistent(true);
+            r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+        }
+        if (isolated && isolatedUid != 0) {
+            // Special case for startIsolatedProcess (internal only) - assume the process
+            // is required by the system server to prevent it being killed.
+            r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
+        }
+        addProcessNameLocked(r);
+        return r;
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
+        return removeProcessNameLocked(name, uid, null);
+    }
+
+    @GuardedBy("mService")
+    final ProcessRecord removeProcessNameLocked(final String name, final int uid,
+            final ProcessRecord expecting) {
+        ProcessRecord old = mProcessNames.get(name, uid);
+        // Only actually remove when the currently recorded value matches the
+        // record that we expected; if it doesn't match then we raced with a
+        // newly created process and we don't want to destroy the new one.
+        if ((expecting == null) || (old == expecting)) {
+            mProcessNames.remove(name, uid);
+        }
+        if (old != null && old.uidRecord != null) {
+            old.uidRecord.numProcs--;
+            if (old.uidRecord.numProcs == 0) {
+                // No more processes using this uid, tell clients it is gone.
+                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
+                        "No more processes in " + old.uidRecord);
+                mService.enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+                EventLogTags.writeAmUidStopped(uid);
+                mService.mActiveUids.remove(uid);
+                mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
+            }
+            old.uidRecord = null;
+        }
+        mIsolatedProcesses.remove(uid);
+        return old;
+    }
+
+    /** Call setCoreSettings on all LRU processes, with the new settings. */
+    @GuardedBy("mService")
+    void updateCoreSettingsLocked(Bundle settings) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord processRecord = mLruProcesses.get(i);
+            try {
+                if (processRecord.thread != null) {
+                    processRecord.thread.setCoreSettings(settings);
+                }
+            } catch (RemoteException re) {
+                /* ignore */
+            }
+        }
+    }
+
+    /**
+     * Kill all background processes except for ones with targetSdk lower than minTargetSdk and
+     * procstate lower than maxProcState.
+     * @param minTargetSdk
+     * @param maxProcState
+     */
+    @GuardedBy("mService")
+    void killAllBackgroundProcessesExceptLocked(int minTargetSdk, int maxProcState) {
+        final ArrayList<ProcessRecord> procs = new ArrayList<>();
+        final int NP = mProcessNames.getMap().size();
+        for (int ip = 0; ip < NP; ip++) {
+            final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+            final int NA = apps.size();
+            for (int ia = 0; ia < NA; ia++) {
+                final ProcessRecord app = apps.valueAt(ia);
+                if (app.removed) {
+                    procs.add(app);
+                } else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
+                        && (maxProcState < 0 || app.setProcState > maxProcState)) {
+                    app.removed = true;
+                    procs.add(app);
+                }
+            }
+        }
+
+        final int N = procs.size();
+        for (int i = 0; i < N; i++) {
+            removeProcessLocked(procs.get(i), false, true, "kill all background except");
+        }
+    }
+
+    /**
+     * Call updateTimePrefs on all LRU processes
+     * @param timePref The time pref to pass to each process
+     */
+    @GuardedBy("mService")
+    void updateAllTimePrefsLocked(int timePref) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.updateTimePrefs(timePref);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to update preferences for: "
+                            + r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void setAllHttpProxyLocked(String host, String port, String exclList, Uri pacFileUrl) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            // Don't dispatch to isolated processes as they can't access
+            // ConnectivityManager and don't have network privileges anyway.
+            if (r.thread != null && !r.isolated) {
+                try {
+                    r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to update http proxy for: " +
+                            r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void clearAllDnsCacheLocked() {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.clearDnsCache();
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void handleAllTrustStorageUpdateLocked() {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null) {
+                try {
+                    r.thread.handleTrustStorageUpdate();
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to handle trust storage update for: " +
+                            r.info.processName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
+            String what, Object obj, ProcessRecord srcApp) {
+        app.lastActivityTime = now;
+
+        if (app.hasActivitiesOrRecentTasks()) {
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
+        }
+
+        int lrui = mLruProcesses.lastIndexOf(app);
+        if (lrui < 0) {
+            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+                    + what + " " + obj + " from " + srcApp);
+            return index;
+        }
+
+        if (lrui >= index) {
+            // Don't want to cause this to move dependent processes *back* in the
+            // list as if they were less frequently used.
+            return index;
+        }
+
+        if (lrui >= mLruProcessActivityStart) {
+            // Don't want to touch dependent processes that are hosting activities.
+            return index;
+        }
+
+        mLruProcesses.remove(lrui);
+        if (index > 0) {
+            index--;
+        }
+        if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+                + " in LRU list: " + app);
+        mLruProcesses.add(index, app);
+        return index;
+    }
+
+    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
+            ProcessRecord client) {
+        final boolean hasActivity = app.hasActivitiesOrRecentTasks() || app.hasClientActivities()
+                || app.treatLikeActivity;
+        final boolean hasService = false; // not impl yet. app.services.size() > 0;
+        if (!activityChange && hasActivity) {
+            // The process has activities, so we are only allowing activity-based adjustments
+            // to move it.  It should be kept in the front of the list with other
+            // processes that have activities, and we don't want those to change their
+            // order except due to activity operations.
+            return;
+        }
+
+        mLruSeq++;
+        final long now = SystemClock.uptimeMillis();
+        app.lastActivityTime = now;
+
+        // First a quick reject: if the app is already at the position we will
+        // put it, then there is nothing to do.
+        if (hasActivity) {
+            final int N = mLruProcesses.size();
+            if (N > 0 && mLruProcesses.get(N - 1) == app) {
+                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
+                return;
+            }
+        } else {
+            if (mLruProcessServiceStart > 0
+                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
+                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
+                return;
+            }
+        }
+
+        int lrui = mLruProcesses.lastIndexOf(app);
+
+        if (app.isPersistent() && lrui >= 0) {
+            // We don't care about the position of persistent processes, as long as
+            // they are in the list.
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
+            return;
+        }
+
+        /* In progress: compute new position first, so we can avoid doing work
+           if the process is not actually going to move.  Not yet working.
+        int addIndex;
+        int nextIndex;
+        boolean inActivity = false, inService = false;
+        if (hasActivity) {
+            // Process has activities, put it at the very tipsy-top.
+            addIndex = mLruProcesses.size();
+            nextIndex = mLruProcessServiceStart;
+            inActivity = true;
+        } else if (hasService) {
+            // Process has services, put it at the top of the service list.
+            addIndex = mLruProcessActivityStart;
+            nextIndex = mLruProcessServiceStart;
+            inActivity = true;
+            inService = true;
+        } else  {
+            // Process not otherwise of interest, it goes to the top of the non-service area.
+            addIndex = mLruProcessServiceStart;
+            if (client != null) {
+                int clientIndex = mLruProcesses.lastIndexOf(client);
+                if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
+                        + app);
+                if (clientIndex >= 0 && addIndex > clientIndex) {
+                    addIndex = clientIndex;
+                }
+            }
+            nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
+        }
+
+        Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
+                + mLruProcessActivityStart + "): " + app);
+        */
+
+        if (lrui >= 0) {
+            if (lrui < mLruProcessActivityStart) {
+                mLruProcessActivityStart--;
+            }
+            if (lrui < mLruProcessServiceStart) {
+                mLruProcessServiceStart--;
+            }
+            /*
+            if (addIndex > lrui) {
+                addIndex--;
+            }
+            if (nextIndex > lrui) {
+                nextIndex--;
+            }
+            */
+            mLruProcesses.remove(lrui);
+        }
+
+        /*
+        mLruProcesses.add(addIndex, app);
+        if (inActivity) {
+            mLruProcessActivityStart++;
+        }
+        if (inService) {
+            mLruProcessActivityStart++;
+        }
+        */
+
+        int nextIndex;
+        if (hasActivity) {
+            final int N = mLruProcesses.size();
+            if ((!app.hasActivities() || app.hasRecentTasks())
+                    && mLruProcessActivityStart < (N - 1)) {
+                // Process doesn't have activities, but has clients with
+                // activities...  move it up, but one below the top (the top
+                // should always have a real activity).
+                if (DEBUG_LRU) Slog.d(TAG_LRU,
+                        "Adding to second-top of LRU activity list: " + app);
+                mLruProcesses.add(N - 1, app);
+                // To keep it from spamming the LRU list (by making a bunch of clients),
+                // we will push down any other entries owned by the app.
+                final int uid = app.info.uid;
+                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
+                    ProcessRecord subProc = mLruProcesses.get(i);
+                    if (subProc.info.uid == uid) {
+                        // We want to push this one down the list.  If the process after
+                        // it is for the same uid, however, don't do so, because we don't
+                        // want them internally to be re-ordered.
+                        if (mLruProcesses.get(i - 1).info.uid != uid) {
+                            if (DEBUG_LRU) Slog.d(TAG_LRU,
+                                    "Pushing uid " + uid + " swapping at " + i + ": "
+                                            + mLruProcesses.get(i) + " : "
+                                            + mLruProcesses.get(i - 1));
+                            ProcessRecord tmp = mLruProcesses.get(i);
+                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
+                            mLruProcesses.set(i - 1, tmp);
+                            i--;
+                        }
+                    } else {
+                        // A gap, we can stop here.
+                        break;
+                    }
+                }
+            } else {
+                // Process has activities, put it at the very tipsy-top.
+                if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
+                mLruProcesses.add(app);
+            }
+            nextIndex = mLruProcessServiceStart;
+        } else if (hasService) {
+            // Process has services, put it at the top of the service list.
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
+            mLruProcesses.add(mLruProcessActivityStart, app);
+            nextIndex = mLruProcessServiceStart;
+            mLruProcessActivityStart++;
+        } else  {
+            // Process not otherwise of interest, it goes to the top of the non-service area.
+            int index = mLruProcessServiceStart;
+            if (client != null) {
+                // If there is a client, don't allow the process to be moved up higher
+                // in the list than that client.
+                int clientIndex = mLruProcesses.lastIndexOf(client);
+                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
+                        + " when updating " + app);
+                if (clientIndex <= lrui) {
+                    // Don't allow the client index restriction to push it down farther in the
+                    // list than it already is.
+                    clientIndex = lrui;
+                }
+                if (clientIndex >= 0 && index > clientIndex) {
+                    index = clientIndex;
+                }
+            }
+            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
+            mLruProcesses.add(index, app);
+            nextIndex = index - 1;
+            mLruProcessActivityStart++;
+            mLruProcessServiceStart++;
+        }
+
+        // If the app is currently using a content provider or service,
+        // bump those processes as well.
+        for (int j = app.connections.size() - 1; j >= 0; j--) {
+            ConnectionRecord cr = app.connections.valueAt(j);
+            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
+                    && cr.binding.service.app != null
+                    && cr.binding.service.app.lruSeq != mLruSeq
+                    && !cr.binding.service.app.isPersistent()) {
+                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+                        now,
+                        nextIndex,
+                        "service connection", cr, app);
+            }
+        }
+        for (int j = app.conProviders.size() - 1; j >= 0; j--) {
+            ContentProviderRecord cpr = app.conProviders.get(j).provider;
+            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
+                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+                        "provider reference", cpr, app);
+            }
+        }
+    }
+
+    final ProcessRecord getLRURecordForAppLocked(IApplicationThread thread) {
+        final IBinder threadBinder = thread.asBinder();
+        // Find the application record.
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord rec = mLruProcesses.get(i);
+            if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
+                return rec;
+            }
+        }
+        return null;
+    }
+
+    boolean haveBackgroundProcessLocked() {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord rec = mLruProcesses.get(i);
+            if (rec.thread != null
+                    && rec.setProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static int procStateToImportance(int procState, int memAdj,
+            ActivityManager.RunningAppProcessInfo currApp,
+            int clientTargetSdk) {
+        int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
+                procState, clientTargetSdk);
+        if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+            currApp.lru = memAdj;
+        } else {
+            currApp.lru = 0;
+        }
+        return imp;
+    }
+
+    @GuardedBy("mService")
+    void fillInProcMemInfoLocked(ProcessRecord app,
+            ActivityManager.RunningAppProcessInfo outInfo,
+            int clientTargetSdk) {
+        outInfo.pid = app.pid;
+        outInfo.uid = app.info.uid;
+        if (mService.mAtmInternal.isHeavyWeightProcess(app.getWindowProcessController())) {
+            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
+        }
+        if (app.isPersistent()) {
+            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
+        }
+        if (app.hasActivities()) {
+            outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
+        }
+        outInfo.lastTrimLevel = app.trimMemoryLevel;
+        int adj = app.curAdj;
+        int procState = app.getCurProcState();
+        outInfo.importance = procStateToImportance(procState, adj, outInfo,
+                clientTargetSdk);
+        outInfo.importanceReasonCode = app.adjTypeCode;
+        outInfo.processState = app.getCurProcState();
+        outInfo.isFocused = (app == mService.getTopAppLocked());
+        outInfo.lastActivityTime = app.lastActivityTime;
+    }
+
+    @GuardedBy("mService")
+    List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesLocked(boolean allUsers,
+            int userId, boolean allUids, int callingUid, int clientTargetSdk) {
+        // Lazy instantiation of list
+        List<ActivityManager.RunningAppProcessInfo> runList = null;
+
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord app = mLruProcesses.get(i);
+            if ((!allUsers && app.userId != userId)
+                    || (!allUids && app.uid != callingUid)) {
+                continue;
+            }
+            if ((app.thread != null) && (!app.isCrashing() && !app.isNotResponding())) {
+                // Generate process state info for running application
+                ActivityManager.RunningAppProcessInfo currApp =
+                        new ActivityManager.RunningAppProcessInfo(app.processName,
+                                app.pid, app.getPackageList());
+                fillInProcMemInfoLocked(app, currApp, clientTargetSdk);
+                if (app.adjSource instanceof ProcessRecord) {
+                    currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+                    currApp.importanceReasonImportance =
+                            ActivityManager.RunningAppProcessInfo.procStateToImportance(
+                                    app.adjSourceProcState);
+                } else if (app.adjSource instanceof ActivityRecord) {
+                    ActivityRecord r = (ActivityRecord)app.adjSource;
+                    if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+                }
+                if (app.adjTarget instanceof ComponentName) {
+                    currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+                }
+                //Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
+                //        + " lru=" + currApp.lru);
+                if (runList == null) {
+                    runList = new ArrayList<>();
+                }
+                runList.add(currApp);
+            }
+        }
+        return runList;
+    }
+
+    @GuardedBy("mService")
+    int getLruSizeLocked() {
+        return mLruProcesses.size();
+    }
+
+    @GuardedBy("mService")
+    void dumpLruListHeaderLocked(PrintWriter pw) {
+        pw.print("  Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
+        pw.print(" total, non-act at ");
+        pw.print(mLruProcesses.size() - mLruProcessActivityStart);
+        pw.print(", non-svc at ");
+        pw.print(mLruProcesses.size() - mLruProcessServiceStart);
+        pw.println("):");
+    }
+
+    @GuardedBy("mService")
+    ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
+        ArrayList<ProcessRecord> procs;
+        if (args != null && args.length > start
+                && args[start].charAt(0) != '-') {
+            procs = new ArrayList<ProcessRecord>();
+            int pid = -1;
+            try {
+                pid = Integer.parseInt(args[start]);
+            } catch (NumberFormatException e) {
+            }
+            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+                ProcessRecord proc = mLruProcesses.get(i);
+                if (proc.pid > 0 && proc.pid == pid) {
+                    procs.add(proc);
+                } else if (allPkgs && proc.pkgList != null
+                        && proc.pkgList.containsKey(args[start])) {
+                    procs.add(proc);
+                } else if (proc.processName.equals(args[start])) {
+                    procs.add(proc);
+                }
+            }
+            if (procs.size() <= 0) {
+                return null;
+            }
+        } else {
+            procs = new ArrayList<ProcessRecord>(mLruProcesses);
+        }
+        return procs;
+    }
+
+    @GuardedBy("mService")
+    void updateApplicationInfoLocked(List<String> packagesToUpdate, int userId,
+            boolean updateFrameworkRes) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            final ProcessRecord app = mLruProcesses.get(i);
+            if (app.thread == null) {
+                continue;
+            }
+
+            if (userId != UserHandle.USER_ALL && app.userId != userId) {
+                continue;
+            }
+
+            final int packageCount = app.pkgList.size();
+            for (int j = 0; j < packageCount; j++) {
+                final String packageName = app.pkgList.keyAt(j);
+                if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
+                    try {
+                        final ApplicationInfo ai = AppGlobals.getPackageManager()
+                                .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
+                        if (ai != null) {
+                            app.thread.scheduleApplicationInfoChanged(ai);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s",
+                                packageName, app));
+                    }
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord r = mLruProcesses.get(i);
+            if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) {
+                try {
+                    r.thread.dispatchPackageBroadcast(cmd, packages);
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 667d3fa..fa7a08b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -18,12 +18,17 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.MY_PID;
 
 import android.app.ActivityManager;
+import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -32,16 +37,19 @@
 import android.os.Binder;
 import android.os.Debug;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -49,7 +57,11 @@
 import com.android.internal.app.procstats.ProcessState;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.server.Watchdog;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -128,7 +140,7 @@
     long lastCachedPss;         // Last computed pss when in cached state.
     long lastCachedSwapPss;     // Last computed SwapPss when in cached state.
     int maxAdj;                 // Maximum OOM adjustment for this process
-    int curRawAdj;              // Current OOM unlimited adjustment for this process
+    private int mCurRawAdj;     // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
     int curAdj;                 // Current OOM adjustment for this process
     int setAdj;                 // Last set OOM adjustment for this process
@@ -136,7 +148,7 @@
     private int mCurSchedGroup; // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
-    int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
+    private int mCurProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
     private int mRepProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
     int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
     int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
@@ -146,19 +158,19 @@
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
-    boolean hasClientActivities;  // Are there any client services with activities?
+    private boolean mHasClientActivities;  // Are there any client services with activities?
     boolean hasStartedServices; // Are there any started services running in this process?
     private boolean mHasForegroundServices; // Running any services that are foreground?
-    boolean foregroundActivities; // Running any activities that are foreground?
+    private boolean mHasForegroundActivities; // Running any activities that are foreground?
     boolean repForegroundActivities; // Last reported foreground activities.
     boolean systemNoUi;         // This is a system process, but not currently showing UI.
     boolean hasShownUi;         // Has UI been shown in this process since it was started?
-    boolean hasTopUi;           // Is this process currently showing a non-activity UI that the user
+    private boolean mHasTopUi;  // Is this process currently showing a non-activity UI that the user
                                 // is interacting with? E.g. The status bar when it is expanded, but
                                 // not when it is minimized. When true the
                                 // process will be set to use the ProcessList#SCHED_GROUP_TOP_APP
                                 // scheduling group to boost performance.
-    boolean hasOverlayUi;       // Is the process currently showing a non-activity UI that
+    private boolean mHasOverlayUi; // Is the process currently showing a non-activity UI that
                                 // overlays on-top of activity UIs on screen. E.g. display a window
                                 // of type
                                 // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY
@@ -171,7 +183,7 @@
                                 // performance, as well as oom adj score will be set to
                                 // ProcessList#VISIBLE_APP_ADJ at minimum to reduce the chance
                                 // of the process getting killed.
-    boolean pendingUiClean;     // Want to clean up resources from showing UI?
+    private boolean mPendingUiClean; // Want to clean up resources from showing UI?
     boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
     boolean treatLikeActivity;  // Bound using BIND_TREAT_LIKE_ACTIVITY
     boolean bad;                // True if disabled in the bad process list
@@ -180,8 +192,8 @@
     boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
     boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
     boolean unlocked;           // True when proc was started in user unlocked state
-    long interactionEventTime;  // The time we sent the last interaction event
-    long fgInteractionTime;     // When we became foreground for interaction purposes
+    private long mInteractionEventTime; // The time we sent the last interaction event
+    private long mFgInteractionTime; // When we became foreground for interaction purposes
     String waitingToKill;       // Process is waiting to be killed when in the bg, and reason
     Object forcingToImportant;  // Token that is forcing this process to be important
     int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
@@ -194,7 +206,7 @@
                                           // process.
     private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
     final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
-    long whenUnimportant;       // When (uptime) the process last became unimportant
+    private long mWhenUnimportant; // When (uptime) the process last became unimportant
     long lastCpuTime;           // How long proc has run CPU at last check
     long curCpuTime;            // How long proc has run CPU most recently
     long lastRequestedGc;       // When we last asked the app to do a gc
@@ -362,7 +374,7 @@
                     pw.print(" initialIdlePss="); pw.println(initialIdlePss);
         }
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
-                pw.print(" curRaw="); pw.print(curRawAdj);
+                pw.print(" curRaw="); pw.print(mCurRawAdj);
                 pw.print(" setRaw="); pw.print(setRawAdj);
                 pw.print(" cur="); pw.print(curAdj);
                 pw.print(" set="); pw.println(setAdj);
@@ -370,38 +382,38 @@
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
-        pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
+        pw.print(prefix); pw.print("curProcState="); pw.print(getCurProcState());
                 pw.print(" mRepProcState="); pw.print(mRepProcState);
                 pw.print(" pssProcState="); pw.print(pssProcState);
                 pw.print(" setProcState="); pw.print(setProcState);
                 pw.print(" lastStateTime=");
                 TimeUtils.formatDuration(lastStateTime, nowUptime, pw);
                 pw.println();
-        if (hasShownUi || pendingUiClean || hasAboveClient || treatLikeActivity) {
+        if (hasShownUi || mPendingUiClean || hasAboveClient || treatLikeActivity) {
             pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
-                    pw.print(" pendingUiClean="); pw.print(pendingUiClean);
+                    pw.print(" pendingUiClean="); pw.print(mPendingUiClean);
                     pw.print(" hasAboveClient="); pw.print(hasAboveClient);
                     pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
         }
-        if (hasTopUi || hasOverlayUi || runningRemoteAnimation) {
-            pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi);
-                    pw.print(" hasOverlayUi="); pw.print(hasOverlayUi);
+        if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
+            pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
+                    pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
                     pw.print(" runningRemoteAnimation="); pw.println(runningRemoteAnimation);
         }
         if (mHasForegroundServices || forcingToImportant != null) {
             pw.print(prefix); pw.print("mHasForegroundServices="); pw.print(mHasForegroundServices);
                     pw.print(" forcingToImportant="); pw.println(forcingToImportant);
         }
-        if (reportedInteraction || fgInteractionTime != 0) {
+        if (reportedInteraction || mFgInteractionTime != 0) {
             pw.print(prefix); pw.print("reportedInteraction=");
             pw.print(reportedInteraction);
-            if (interactionEventTime != 0) {
+            if (mInteractionEventTime != 0) {
                 pw.print(" time=");
-                TimeUtils.formatDuration(interactionEventTime, SystemClock.elapsedRealtime(), pw);
+                TimeUtils.formatDuration(mInteractionEventTime, SystemClock.elapsedRealtime(), pw);
             }
-            if (fgInteractionTime != 0) {
+            if (mFgInteractionTime != 0) {
                 pw.print(" fgInteractionTime=");
-                TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw);
+                TimeUtils.formatDuration(mFgInteractionTime, SystemClock.elapsedRealtime(), pw);
             }
             pw.println();
         }
@@ -409,9 +421,9 @@
             pw.print(prefix); pw.print("persistent="); pw.print(mPersistent);
                     pw.print(" removed="); pw.println(removed);
         }
-        if (hasClientActivities || foregroundActivities || repForegroundActivities) {
-            pw.print(prefix); pw.print("hasClientActivities="); pw.print(hasClientActivities);
-                    pw.print(" foregroundActivities="); pw.print(foregroundActivities);
+        if (mHasClientActivities || mHasForegroundActivities || repForegroundActivities) {
+            pw.print(prefix); pw.print("hasClientActivities="); pw.print(mHasClientActivities);
+                    pw.print(" foregroundActivities="); pw.print(mHasForegroundActivities);
                     pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
         }
         if (lastProviderTime > 0) {
@@ -438,7 +450,7 @@
                         TimeUtils.formatDuration(curCpuTime - lastCpuTime, pw);
                     }
                     pw.print(" whenUnimportant=");
-                    TimeUtils.formatDuration(whenUnimportant - nowUptime, pw);
+                    TimeUtils.formatDuration(mWhenUnimportant - nowUptime, pw);
                     pw.println();
         }
         pw.print(prefix); pw.print("lastRequestedGc=");
@@ -531,7 +543,7 @@
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
         maxAdj = ProcessList.UNKNOWN_ADJ;
-        curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
+        mCurRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
         curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
         mPersistent = false;
         removed = false;
@@ -723,7 +735,7 @@
             if (pid > 0) {
                 EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
                 Process.killProcessQuiet(pid);
-                ActivityManagerService.killProcessGroup(uid, pid);
+                ProcessList.killProcessGroup(uid, pid);
             } else {
                 pendingStart = false;
             }
@@ -735,6 +747,7 @@
         }
     }
 
+    @Override
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(ProcessRecordProto.PID, pid);
@@ -825,6 +838,13 @@
         return null;
     }
 
+    @Override
+    public void addPackage(String pkg, long versionCode) {
+        synchronized (mService) {
+            addPackage(pkg, versionCode, mService.mProcessStats);
+        }
+    }
+
     /*
      *  Return true if package has been added false if not
      */
@@ -857,7 +877,8 @@
 
     public void forceProcessStateUpTo(int newState) {
         if (mRepProcState > newState) {
-            curProcState = mRepProcState = newState;
+            mRepProcState = newState;
+            setCurProcState(newState);
             for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
                 StatsLog.write(StatsLog.PROCESS_STATE_CHANGED,
                         uid, processName, pkgList.keyAt(ipkg),
@@ -931,6 +952,15 @@
         return mCurSchedGroup;
     }
 
+    void setCurProcState(int curProcState) {
+        mCurProcState = curProcState;
+        mWindowProcessController.setCurrentProcState(mCurProcState);
+    }
+
+    int getCurProcState() {
+        return mCurProcState;
+    }
+
     void setReportedProcState(int repProcState) {
         mRepProcState = repProcState;
         for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
@@ -991,6 +1021,69 @@
         return mHasForegroundServices;
     }
 
+    void setHasForegroundActivities(boolean hasForegroundActivities) {
+        mHasForegroundActivities = hasForegroundActivities;
+        mWindowProcessController.setHasForegroundActivities(hasForegroundActivities);
+    }
+
+    boolean hasForegroundActivities() {
+        return mHasForegroundActivities;
+    }
+
+    void setHasClientActivities(boolean hasClientActivities) {
+        mHasClientActivities = hasClientActivities;
+        mWindowProcessController.setHasClientActivities(hasClientActivities);
+    }
+
+    boolean hasClientActivities() {
+        return mHasClientActivities;
+    }
+
+    void setHasTopUi(boolean hasTopUi) {
+        mHasTopUi = hasTopUi;
+        mWindowProcessController.setHasTopUi(hasTopUi);
+    }
+
+    boolean hasTopUi() {
+        return mHasTopUi;
+    }
+
+    void setHasOverlayUi(boolean hasOverlayUi) {
+        mHasOverlayUi = hasOverlayUi;
+        mWindowProcessController.setHasOverlayUi(hasOverlayUi);
+    }
+
+    boolean hasOverlayUi() {
+        return mHasOverlayUi;
+    }
+
+    void setInteractionEventTime(long interactionEventTime) {
+        mInteractionEventTime = interactionEventTime;
+        mWindowProcessController.setInteractionEventTime(interactionEventTime);
+    }
+
+    long getInteractionEventTime() {
+        return mInteractionEventTime;
+    }
+
+    void setFgInteractionTime(long fgInteractionTime) {
+        mFgInteractionTime = fgInteractionTime;
+        mWindowProcessController.setFgInteractionTime(fgInteractionTime);
+    }
+
+    long getFgInteractionTime() {
+        return mFgInteractionTime;
+    }
+
+    void setWhenUnimportant(long whenUnimportant) {
+        mWhenUnimportant = whenUnimportant;
+        mWindowProcessController.setWhenUnimportant(whenUnimportant);
+    }
+
+    long getWhenUnimportant() {
+        return mWhenUnimportant;
+    }
+
     void setDebugging(boolean debugging) {
         mDebugging = debugging;
         mWindowProcessController.setDebugging(debugging);
@@ -1018,6 +1111,15 @@
         return mInstr;
     }
 
+    void setCurRawAdj(int curRawAdj) {
+        mCurRawAdj = curRawAdj;
+        mWindowProcessController.setPerceptible(curRawAdj <= ProcessList.PERCEPTIBLE_APP_ADJ);
+    }
+
+    int getCurRawAdj() {
+        return mCurRawAdj;
+    }
+
     @Override
     public void clearProfilerIfNeeded() {
         synchronized (mService) {
@@ -1039,14 +1141,19 @@
     @Override
     public void setPendingUiClean(boolean pendingUiClean) {
         synchronized (mService) {
-            this.pendingUiClean = true;
+            mPendingUiClean = pendingUiClean;
+            mWindowProcessController.setPendingUiClean(pendingUiClean);
         }
     }
 
+    boolean hasPendingUiClean() {
+        return mPendingUiClean;
+    }
+
     @Override
     public void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
         synchronized (mService) {
-            pendingUiClean = true;
+            setPendingUiClean(true);
             forceProcessStateUpTo(newState);
         }
     }
@@ -1059,7 +1166,7 @@
                 mService.mServices.updateServiceConnectionActivitiesLocked(this);
             }
             if (updateLru) {
-                mService.updateLruProcessLocked(this, activityChange, null);
+                mService.mProcessList.updateLruProcessLocked(this, activityChange, null);
             }
             if (updateOomAdj) {
                 mService.updateOomAdjLocked();
@@ -1078,7 +1185,315 @@
      * Returns the total time (in milliseconds) spent executing in both user and system code.
      * Safe to call without lock held.
      */
+    @Override
     public long getCpuTime() {
         return mService.mProcessCpuTracker.getCpuTimeForPid(pid);
     }
+
+    @Override
+    public void clearWaitingToKill() {
+        synchronized (mService) {
+            waitingToKill = null;
+        }
+    }
+
+    @Override
+    public ProfilerInfo onStartActivity(int topProcessState) {
+        synchronized (mService) {
+            ProfilerInfo profilerInfo = null;
+            if (mService.mProfileApp != null && mService.mProfileApp.equals(processName)) {
+                if (mService.mProfileProc == null || mService.mProfileProc == this) {
+                    mService.mProfileProc = this;
+                    final ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
+                    if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
+                        if (profilerInfoSvc.profileFd != null) {
+                            try {
+                                profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
+                            } catch (IOException e) {
+                                profilerInfoSvc.closeFd();
+                            }
+                        }
+
+                        profilerInfo = new ProfilerInfo(profilerInfoSvc);
+                    }
+                }
+            }
+
+            hasShownUi = true;
+            setPendingUiClean(true);
+            forceProcessStateUpTo(topProcessState);
+
+            return profilerInfo;
+        }
+    }
+
+    @Override
+    public void appDied() {
+        synchronized (mService) {
+            mService.appDiedLocked(this);
+        }
+    }
+
+    public long getInputDispatchingTimeout() {
+        return mWindowProcessController.getInputDispatchingTimeout();
+    }
+
+    void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
+            String parentShortComponentName, WindowProcessController parentProcess,
+            boolean aboveSystem, String annotation) {
+        ArrayList<Integer> firstPids = new ArrayList<>(5);
+        SparseArray<Boolean> lastPids = new SparseArray<>(20);
+
+        if (mService.mActivityTaskManager.mController != null) {
+            try {
+                // 0 == continue, -1 = kill process immediately
+                int res = mService.mActivityTaskManager.mController.appEarlyNotResponding(
+                        processName, pid, annotation);
+                if (res < 0 && pid != MY_PID) {
+                    kill("anr", true);
+                }
+            } catch (RemoteException e) {
+                mService.mActivityTaskManager.mController = null;
+                Watchdog.getInstance().setActivityController(null);
+            }
+        }
+
+        long anrTime = SystemClock.uptimeMillis();
+        if (ActivityManagerService.MONITOR_CPU_USAGE) {
+            mService.updateCpuStatsNow();
+        }
+
+        // Unless configured otherwise, swallow ANRs in background processes & kill the process.
+        boolean showBackground = Settings.Secure.getInt(mService.mContext.getContentResolver(),
+                Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+
+        boolean isSilentANR;
+
+        synchronized (mService) {
+            // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
+            if (mService.mActivityTaskManager.mShuttingDown) {
+                Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+                return;
+            } else if (isNotResponding()) {
+                Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+                return;
+            } else if (isCrashing()) {
+                Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+                return;
+            } else if (killedByAm) {
+                Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+                return;
+            } else if (killed) {
+                Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+                return;
+            }
+
+            // In case we come through here for the same app before completing
+            // this one, mark as anring now so we will bail out.
+            setNotResponding(true);
+
+            // Log the ANR to the event log.
+            EventLog.writeEvent(EventLogTags.AM_ANR, userId, pid, processName, info.flags,
+                    annotation);
+
+            // Dump thread traces as quickly as we can, starting with "interesting" processes.
+            firstPids.add(pid);
+
+            // Don't dump other PIDs if it's a background ANR
+            isSilentANR = !showBackground && !isInterestingForBackgroundTraces();
+            if (!isSilentANR) {
+                int parentPid = pid;
+                if (parentProcess != null && parentProcess.getPid() > 0) {
+                    parentPid = parentProcess.getPid();
+                }
+                if (parentPid != pid) firstPids.add(parentPid);
+
+                if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
+
+                for (int i = mService.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
+                    ProcessRecord r = mService.mProcessList.mLruProcesses.get(i);
+                    if (r != null && r.thread != null) {
+                        int myPid = r.pid;
+                        if (myPid > 0 && myPid != pid && myPid != parentPid && myPid != MY_PID) {
+                            if (r.isPersistent()) {
+                                firstPids.add(myPid);
+                                if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
+                            } else if (r.treatLikeActivity) {
+                                firstPids.add(myPid);
+                                if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
+                            } else {
+                                lastPids.put(myPid, Boolean.TRUE);
+                                if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Log the ANR to the main log.
+        StringBuilder info = new StringBuilder();
+        info.setLength(0);
+        info.append("ANR in ").append(processName);
+        if (activityShortComponentName != null) {
+            info.append(" (").append(activityShortComponentName).append(")");
+        }
+        info.append("\n");
+        info.append("PID: ").append(pid).append("\n");
+        if (annotation != null) {
+            info.append("Reason: ").append(annotation).append("\n");
+        }
+        if (parentShortComponentName != null
+                && parentShortComponentName.equals(activityShortComponentName)) {
+            info.append("Parent: ").append(parentShortComponentName).append("\n");
+        }
+
+        ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+
+        // don't dump native PIDs for background ANRs unless it is the process of interest
+        String[] nativeProcs = null;
+        if (isSilentANR) {
+            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
+                if (NATIVE_STACKS_OF_INTEREST[i].equals(processName)) {
+                    nativeProcs = new String[] { processName };
+                    break;
+                }
+            }
+        } else {
+            nativeProcs = NATIVE_STACKS_OF_INTEREST;
+        }
+
+        int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
+        ArrayList<Integer> nativePids = null;
+
+        if (pids != null) {
+            nativePids = new ArrayList<>(pids.length);
+            for (int i : pids) {
+                nativePids.add(i);
+            }
+        }
+
+        // For background ANRs, don't pass the ProcessCpuTracker to
+        // avoid spending 1/2 second collecting stats to rank lastPids.
+        File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+                (isSilentANR) ? null : processCpuTracker, (isSilentANR) ? null : lastPids,
+                nativePids);
+
+        String cpuInfo = null;
+        if (ActivityManagerService.MONITOR_CPU_USAGE) {
+            mService.updateCpuStatsNow();
+            synchronized (mService.mProcessCpuTracker) {
+                cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
+            }
+            info.append(processCpuTracker.printCurrentLoad());
+            info.append(cpuInfo);
+        }
+
+        info.append(processCpuTracker.printCurrentState(anrTime));
+
+        Slog.e(TAG, info.toString());
+        if (tracesFile == null) {
+            // There is no trace file, so dump (only) the alleged culprit's threads to the log
+            Process.sendSignal(pid, Process.SIGNAL_QUIT);
+        }
+
+        StatsLog.write(StatsLog.ANR_OCCURRED, uid, processName,
+                activityShortComponentName == null ? "unknown": activityShortComponentName,
+                annotation,
+                (this.info != null) ? (this.info.isInstantApp()
+                        ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
+                        : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
+                        : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
+                isInterestingToUserLocked()
+                        ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
+                        : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND);
+        final ProcessRecord parentPr = parentProcess != null
+                ? (ProcessRecord) parentProcess.mOwner : null;
+        mService.addErrorToDropBox("anr", this, processName, activityShortComponentName,
+                parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null);
+
+        if (mService.mActivityTaskManager.mController != null) {
+            try {
+                // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
+                int res = mService.mActivityTaskManager.mController.appNotResponding(
+                        processName, pid, info.toString());
+                if (res != 0) {
+                    if (res < 0 && pid != MY_PID) {
+                        kill("anr", true);
+                    } else {
+                        synchronized (mService) {
+                            mService.mServices.scheduleServiceTimeoutLocked(this);
+                        }
+                    }
+                    return;
+                }
+            } catch (RemoteException e) {
+                mService.mActivityTaskManager.mController = null;
+                Watchdog.getInstance().setActivityController(null);
+            }
+        }
+
+        synchronized (mService) {
+            mService.mBatteryStatsService.noteProcessAnr(processName, uid);
+
+            if (isSilentANR) {
+                kill("bg anr", true);
+                return;
+            }
+
+            // Set the app's notResponding state, and look up the errorReportReceiver
+            makeAppNotRespondingLocked(activityShortComponentName,
+                    annotation != null ? "ANR " + annotation : "ANR", info.toString());
+
+            // Bring up the infamous App Not Responding dialog
+            Message msg = Message.obtain();
+            msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
+            msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);
+
+            mService.mUiHandler.sendMessage(msg);
+        }
+    }
+
+    private void makeAppNotRespondingLocked(String activity, String shortMsg, String longMsg) {
+        setNotResponding(true);
+        notRespondingReport = mService.mAppErrors.generateProcessError(this,
+                ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
+                activity, shortMsg, longMsg, null);
+        startAppProblemLocked();
+        getWindowProcessController().stopFreezingActivities();
+    }
+
+    void startAppProblemLocked() {
+        // If this app is not running under the current user, then we can't give it a report button
+        // because that would require launching the report UI under a different user.
+        errorReportReceiver = null;
+
+        for (int userId : mService.mUserController.getCurrentProfileIds()) {
+            if (this.userId == userId) {
+                errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
+                        mService.mContext, info.packageName, info.flags);
+            }
+        }
+        mService.skipCurrentReceiverLocked(this);
+    }
+
+    private boolean isInterestingForBackgroundTraces() {
+        // The system_server is always considered interesting.
+        if (pid == MY_PID) {
+            return true;
+        }
+
+        // A package is considered interesting if any of the following is true :
+        //
+        // - It's displaying an activity.
+        // - It's the SystemUI.
+        // - It has an overlay or a top UI visible.
+        //
+        // NOTE: The check whether a given ProcessRecord belongs to the systemui
+        // process is a bit of a kludge, but the same pattern seems repeated at
+        // several places in the system server.
+        return isInterestingToUserLocked() ||
+                (info != null && "com.android.systemui".equals(info.packageName))
+                || (hasTopUi() || hasOverlayUi());
+    }
 }
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index dd13e98..57f939f 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -33,15 +33,15 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -103,7 +103,7 @@
  *                                  // 'X' tasks are trimmed.
  */
 class RecentTasks {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_ATM;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index fa0cb47..1152165 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -21,9 +21,9 @@
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -46,7 +46,7 @@
  */
 public class SafeActivityOptions {
 
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_ATM;
 
     private final int mOriginalCallingPid;
     private final int mOriginalCallingUid;
diff --git a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
index fd34d18..111adec 100644
--- a/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/am/TaskLaunchParamsModifier.java
@@ -35,8 +35,8 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -59,7 +59,7 @@
  * The class that defines the default launch params for tasks.
  */
 class TaskLaunchParamsModifier implements LaunchParamsModifier {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
     private static final boolean DEBUG = false;
 
     // A mask for SUPPORTS_SCREEN that indicates the activity supports resize.
@@ -114,6 +114,26 @@
     private int calculate(TaskRecord task, ActivityInfo.WindowLayout layout,
             ActivityRecord activity, ActivityRecord source, ActivityOptions options,
             LaunchParams currentParams, LaunchParams outParams) {
+        final ActivityRecord root;
+        if (task != null) {
+            root = task.getRootActivity() == null ? activity : task.getRootActivity();
+        } else {
+            root = activity;
+        }
+
+        // TODO: Investigate whether we can safely ignore all cases where we don't have root
+        // activity available. Note we can't know if the bounds are valid if we're not sure of the
+        // requested orientation of the root activity. Therefore if we found such a case we may need
+        // to pass the activity into this modifier in that case.
+        if (root == null) {
+            // There is a case that can lead us here. The caller is moving the top activity that is
+            // in a task that has multiple activities to PIP mode. For that the caller is creating a
+            // new task to host the activity so that we only move the top activity to PIP mode and
+            // keep other activities in the previous task. There is no point to apply the launch
+            // logic in this case.
+            return RESULT_SKIP;
+        }
+
         // STEP 1: Determine the display to launch the activity/task.
         final int displayId = getPreferredLaunchDisplay(options, source, currentParams);
         outParams.mPreferredDisplayId = displayId;
@@ -123,24 +143,18 @@
                     + display.getWindowingMode());
         }
 
-        final ActivityRecord root;
-        if (task != null) {
-            root = (task.getRootActivity() == null ? activity : task.getRootActivity());
-        } else {
-            root = activity;
-        }
         // STEP 2: Resolve launch windowing mode.
         // STEP 2.1: Determine if any parameter has specified initial bounds. That might be the
-        // launch bounds from activity options, or size/gravity passed in layout. It also treat the
+        // launch bounds from activity options, or size/gravity passed in layout. It also treats the
         // launch windowing mode in options as a suggestion for future resolution.
         int launchMode = options != null ? options.getLaunchWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
         // hasInitialBounds is set if either activity options or layout has specified bounds. If
         // that's set we'll skip some adjustments later to avoid overriding the initial bounds.
         boolean hasInitialBounds = false;
-        final boolean canApplyFreeformPolicy =
-                canApplyFreeformWindowPolicy(display, root, launchMode);
-        if (mSupervisor.canUseActivityOptionsLaunchBounds(options) && canApplyFreeformPolicy) {
+        final boolean canApplyFreeformPolicy = canApplyFreeformWindowPolicy(display, launchMode);
+        if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
+                && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
             hasInitialBounds = true;
             launchMode = launchMode == WINDOWING_MODE_UNDEFINED
                     ? WINDOWING_MODE_FREEFORM
@@ -279,10 +293,14 @@
         return displayId;
     }
 
-    private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display,
-            @NonNull ActivityRecord root, int launchMode) {
-        return display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM
-                || root.isResizeable();
+    private boolean canApplyFreeformWindowPolicy(@NonNull ActivityDisplay display, int launchMode) {
+        return mSupervisor.mService.mSupportsFreeformWindowManagement
+                && (display.inFreeformWindowingMode() || launchMode == WINDOWING_MODE_FREEFORM);
+    }
+
+    private boolean canApplyPipWindowPolicy(int launchMode) {
+        return mSupervisor.mService.mSupportsPictureInPicture
+                && launchMode == WINDOWING_MODE_PINNED;
     }
 
     private void getLayoutBounds(@NonNull ActivityDisplay display, @NonNull ActivityRecord root,
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 05b0d59..5f59163 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -45,16 +46,16 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP;
@@ -129,7 +130,7 @@
 
 // TODO: Make package private again once move to WM package is complete.
 public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_ATM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
@@ -172,7 +173,6 @@
     // code.
     private static final int PERSIST_TASK_VERSION = 1;
 
-    static final int INVALID_TASK_ID = -1;
     private static final int INVALID_MIN_SIZE = -1;
 
     /**
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 3b859ed..6cb1097 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -18,7 +18,6 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
-import android.app.ActivityManagerProto;
 import android.content.pm.PackageManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -27,14 +26,14 @@
 import android.util.proto.ProtoUtils;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.ActivityTaskManagerInternal;
 
 /**
  * Overall information about a uid that has actively running processes.
  */
 public final class UidRecord {
     final int uid;
-    int curProcState;
+    private int mCurProcState;
     int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
     long lastBackgroundTime;
     boolean ephemeral;
@@ -44,11 +43,12 @@
     boolean idle;
     boolean setIdle;
     int numProcs;
+    final ActivityTaskManagerInternal mAtmInternal;
 
     /**
-     * Sequence number associated with the {@link #curProcState}. This is incremented using
+     * Sequence number associated with the {@link #mCurProcState}. This is incremented using
      * {@link ActivityManagerService#mProcStateSeqCounter}
-     * when {@link #curProcState} changes from background to foreground or vice versa.
+     * when {@link #mCurProcState} changes from background to foreground or vice versa.
      */
     @GuardedBy("networkStateUpdate")
     long curProcStateSeq;
@@ -117,14 +117,26 @@
     ChangeItem pendingChange;
     int lastReportedChange;
 
-    public UidRecord(int _uid) {
+    public UidRecord(int _uid, ActivityTaskManagerInternal atmInternal) {
         uid = _uid;
         idle = true;
+        mAtmInternal = atmInternal;
         reset();
     }
 
+    public int getCurProcState() {
+        return mCurProcState;
+    }
+
+    public void setCurProcState(int curProcState) {
+        mCurProcState = curProcState;
+        if (mAtmInternal != null) {
+            mAtmInternal.onUidProcStateChanged(uid, curProcState);
+        }
+    }
+
     public void reset() {
-        curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+        setCurProcState(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
         foregroundServices = false;
     }
 
@@ -148,7 +160,7 @@
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(UidRecordProto.UID, uid);
-        proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(curProcState));
+        proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(mCurProcState));
         proto.write(UidRecordProto.EPHEMERAL, ephemeral);
         proto.write(UidRecordProto.FG_SERVICES, foregroundServices);
         proto.write(UidRecordProto.WHILELIST, curWhitelist);
@@ -178,7 +190,7 @@
         sb.append(' ');
         UserHandle.formatUid(sb, uid);
         sb.append(' ');
-        sb.append(ProcessList.makeProcStateString(curProcState));
+        sb.append(ProcessList.makeProcStateString(mCurProcState));
         if (ephemeral) {
             sb.append(" ephemeral");
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3a897c4..d2dd3db 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2242,21 +2242,15 @@
         }
 
         void stackSupervisorRemoveUser(int userId) {
-            synchronized (mService) {
-                mService.mStackSupervisor.removeUserLocked(userId);
-            }
+            mService.mAtmInternal.removeUser(userId);
         }
 
         protected boolean stackSupervisorSwitchUser(int userId, UserState uss) {
-            synchronized (mService) {
-                return mService.mStackSupervisor.switchUserLocked(userId, uss);
-            }
+            return mService.mAtmInternal.switchUser(userId, uss);
         }
 
         protected void stackSupervisorResumeFocusedStackTopActivity() {
-            synchronized (mService) {
-                mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            }
+            mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
         }
 
         protected void clearAllLockedTasks(String reason) {
diff --git a/services/core/java/com/android/server/am/VrController.java b/services/core/java/com/android/server/am/VrController.java
index 366f95a..51d86d6 100644
--- a/services/core/java/com/android/server/am/VrController.java
+++ b/services/core/java/com/android/server/am/VrController.java
@@ -248,9 +248,9 @@
      *
      * @param tid the tid of the thread to set, or 0 to unset the current thread.
      * @param pid the pid of the process owning the thread to set.
-     * @param proc the ProcessRecord of the process owning the thread to set.
+     * @param proc the process owning the thread to set.
      */
-    public void setPersistentVrThreadLocked(int tid, int pid, ProcessRecord proc) {
+    public void setPersistentVrThreadLocked(int tid, int pid, WindowProcessController proc) {
         if (!hasPersistentVrFlagSet()) {
             Slog.w(TAG, "Persistent VR thread may only be set in persistent VR mode!");
             return;
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index f6f4db6..94f1002 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -17,23 +17,29 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.view.Display.INVALID_DISPLAY;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
 import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityTaskManagerService
+        .INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS;
+import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
+import android.app.ProfilerInfo;
 import android.app.servertransaction.ConfigurationChangeItem;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -43,10 +49,12 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.wm.ConfigurationContainer;
+import com.android.server.wm.ConfigurationContainerListener;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -62,8 +70,9 @@
  * window manager so the window manager lock is held and appropriate permissions are checked before
  * calls are allowed to proceed.
  */
-public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer> {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_AM;
+public class WindowProcessController extends ConfigurationContainer<ConfigurationContainer>
+        implements ConfigurationContainerListener {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowProcessController" : TAG_ATM;
     private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
 
@@ -87,6 +96,8 @@
     private volatile IApplicationThread mThread;
     // Currently desired scheduling class
     private volatile int mCurSchedGroup;
+    // Currently computed process state
+    private volatile int mCurProcState = PROCESS_STATE_NONEXISTENT;
     // Last reported process state;
     private volatile int mRepProcState = PROCESS_STATE_NONEXISTENT;
     // are we in the process of crashing?
@@ -99,10 +110,34 @@
     private volatile String mRequiredAbi;
     // Running any services that are foreground?
     private volatile boolean mHasForegroundServices;
+    // Running any activities that are foreground?
+    private volatile boolean mHasForegroundActivities;
+    // Are there any client services with activities?
+    private volatile boolean mHasClientActivities;
+    // Is this process currently showing a non-activity UI that the user is interacting with?
+    // E.g. The status bar when it is expanded, but not when it is minimized. When true the process
+    // will be set to use the ProcessList#SCHED_GROUP_TOP_APP scheduling group to boost performance.
+    private volatile boolean mHasTopUi;
+    // Is the process currently showing a non-activity UI that overlays on-top of activity UIs on
+    // screen. E.g. display a window of type
+    // android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY When true the process will
+    // oom adj score will be set to ProcessList#PERCEPTIBLE_APP_ADJ at minimum to reduce the chance
+    // of the process getting killed.
+    private volatile boolean mHasOverlayUi;
+    // Want to clean up resources from showing UI?
+    private volatile boolean mPendingUiClean;
+    // The time we sent the last interaction event
+    private volatile long mInteractionEventTime;
+    // When we became foreground for interaction purposes
+    private volatile long mFgInteractionTime;
+    // When (uptime) the process last became unimportant
+    private volatile long mWhenUnimportant;
     // was app launched for debugging?
     private volatile boolean mDebugging;
     // Active instrumentation running in process?
     private volatile boolean mInstrumenting;
+    // This process it perceptible by the user.
+    private volatile boolean mPerceptible;
     // Set to true when process was launched with a wrapper attached
     private volatile boolean mUsingWrapper;
 
@@ -116,6 +151,8 @@
 
     // Last configuration that was reported to the process.
     private final Configuration mLastReportedConfiguration;
+    // Registered display id as a listener to override config change
+    private int mDisplayId;
 
     WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info, String name,
             int uid, int userId, Object owner, WindowProcessListener listener,
@@ -128,6 +165,7 @@
         mListener = listener;
         mAtm = atm;
         mLastReportedConfiguration = new Configuration();
+        mDisplayId = INVALID_DISPLAY;
         if (config != null) {
             onConfigurationChanged(config);
         }
@@ -161,6 +199,14 @@
         return mCurSchedGroup;
     }
 
+    public void setCurrentProcState(int curProcState) {
+        mCurProcState = curProcState;
+    }
+
+    int getCurrentProcState() {
+        return mCurProcState;
+    }
+
     public void setReportedProcState(int repProcState) {
         mRepProcState = repProcState;
     }
@@ -201,6 +247,78 @@
         return mHasForegroundServices;
     }
 
+    public void setHasForegroundActivities(boolean hasForegroundActivities) {
+        mHasForegroundActivities = hasForegroundActivities;
+    }
+
+    boolean hasForegroundActivities() {
+        return mHasForegroundActivities;
+    }
+
+    public void setHasClientActivities(boolean hasClientActivities) {
+        mHasClientActivities = hasClientActivities;
+    }
+
+    boolean hasClientActivities() {
+        return mHasClientActivities;
+    }
+
+    public void setHasTopUi(boolean hasTopUi) {
+        mHasTopUi = hasTopUi;
+    }
+
+    boolean hasTopUi() {
+        return mHasTopUi;
+    }
+
+    public void setHasOverlayUi(boolean hasOverlayUi) {
+        mHasOverlayUi = hasOverlayUi;
+    }
+
+    boolean hasOverlayUi() {
+        return mHasOverlayUi;
+    }
+
+    public void setPendingUiClean(boolean hasPendingUiClean) {
+        mPendingUiClean = hasPendingUiClean;
+    }
+
+    boolean hasPendingUiClean() {
+        return mPendingUiClean;
+    }
+
+    void postPendingUiCleanMsg(boolean pendingUiClean) {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
+        mAtm.mH.sendMessage(m);
+    }
+
+    public void setInteractionEventTime(long interactionEventTime) {
+        mInteractionEventTime = interactionEventTime;
+    }
+
+    long getInteractionEventTime() {
+        return mInteractionEventTime;
+    }
+
+    public void setFgInteractionTime(long fgInteractionTime) {
+        mFgInteractionTime = fgInteractionTime;
+    }
+
+    long getFgInteractionTime() {
+        return mFgInteractionTime;
+    }
+
+    public void setWhenUnimportant(long whenUnimportant) {
+        mWhenUnimportant = whenUnimportant;
+    }
+
+    long getWhenUnimportant() {
+        return mWhenUnimportant;
+    }
+
     public void setRequiredAbi(String requiredAbi) {
         mRequiredAbi = requiredAbi;
     }
@@ -233,6 +351,14 @@
         return mInstrumenting;
     }
 
+    public void setPerceptible(boolean perceptible) {
+        mPerceptible = perceptible;
+    }
+
+    boolean isPerceptible() {
+        return mPerceptible;
+    }
+
     @Override
     protected int getChildCount() {
         return 0;
@@ -500,12 +626,19 @@
             final int activitiesSize = mActivities.size();
             for (int i = activitiesSize - 1; i >= 0; i--) {
                 final ActivityRecord r = mActivities.get(i);
-                if (r.mRelaunchReason != ActivityRecord.RELAUNCH_REASON_NONE) {
+                if (r.mRelaunchReason != RELAUNCH_REASON_NONE) {
                     return r.mRelaunchReason;
                 }
             }
         }
-        return ActivityRecord.RELAUNCH_REASON_NONE;
+        return RELAUNCH_REASON_NONE;
+    }
+
+    public long getInputDispatchingTimeout() {
+        synchronized (mAtm.mGlobalLock) {
+            return isInstrumenting() || isUsingWrapper()
+                    ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
+        }
     }
 
     void clearProfilerIfNeeded() {
@@ -532,14 +665,6 @@
                 WindowProcessListener::updateServiceConnectionActivities, mListener));
     }
 
-    void setPendingUiClean(boolean pendingUiClean) {
-        if (mListener == null) return;
-        // Posting on handler so WM lock isn't held when we call into AM.
-        final Message m = PooledLambda.obtainMessage(
-                WindowProcessListener::setPendingUiClean, mListener, pendingUiClean);
-        mAtm.mH.sendMessage(m);
-    }
-
     void setPendingUiCleanAndForceProcessStateUpTo(int newState) {
         if (mListener == null) return;
         // Posting on handler so WM lock isn't held when we call into AM.
@@ -557,6 +682,59 @@
         mAtm.mH.sendMessage(m);
     }
 
+    void clearWaitingToKill() {
+        if (mListener == null) return;
+        // Posting on handler so WM lock isn't held when we call into AM.
+        final Message m = PooledLambda.obtainMessage(
+                WindowProcessListener::clearWaitingToKill, mListener);
+        mAtm.mH.sendMessage(m);
+    }
+
+    void addPackage(String pkg, long versionCode) {
+        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+        if (mListener == null) return;
+        mListener.addPackage(pkg, versionCode);
+    }
+
+    ProfilerInfo onStartActivity(int topProcessState) {
+        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+        if (mListener == null) return null;
+        return mListener.onStartActivity(topProcessState);
+    }
+
+    public void appDied() {
+        // TODO(b/80414790): Calling directly into AM for now which can lead to deadlock once we are
+        // using WM lock. Need to figure-out if it is okay to do this asynchronously.
+        if (mListener == null) return;
+        mListener.appDied();
+    }
+
+    void registerDisplayConfigurationListenerLocked(ActivityDisplay activityDisplay) {
+        if (activityDisplay == null) {
+            return;
+        }
+        // A process can only register to one display to listener to the override configuration
+        // change. Unregister existing listener if it has one before register the new one.
+        unregisterDisplayConfigurationListenerLocked();
+        mDisplayId = activityDisplay.mDisplayId;
+        activityDisplay.registerConfigurationChangeListener(this);
+    }
+
+    private void unregisterDisplayConfigurationListenerLocked() {
+        if (mDisplayId == INVALID_DISPLAY) {
+            return;
+        }
+        final ActivityDisplay activityDisplay =
+                mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+        if (activityDisplay != null) {
+            mAtm.mStackSupervisor.getActivityDisplay(
+                    mDisplayId).unregisterConfigurationChangeListener(this);
+        }
+        mDisplayId = INVALID_DISPLAY;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newGlobalConfig) {
         super.onConfigurationChanged(newGlobalConfig);
@@ -654,4 +832,9 @@
         pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        if (mListener != null) {
+            mListener.writeToProto(proto, fieldId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/WindowProcessListener.java b/services/core/java/com/android/server/am/WindowProcessListener.java
index 2de3e37..4a7e6e8 100644
--- a/services/core/java/com/android/server/am/WindowProcessListener.java
+++ b/services/core/java/com/android/server/am/WindowProcessListener.java
@@ -16,6 +16,10 @@
 
 package com.android.server.am;
 
+import android.app.ProfilerInfo;
+import android.content.pm.ApplicationInfo;
+import android.util.proto.ProtoOutputStream;
+
 /**
  * Interface used by the owner/creator of a process that owns windows to listen to changes from the
  * WM side.
@@ -47,4 +51,17 @@
 
     /** Returns the total time (in milliseconds) spent executing in both user and system code. */
     long getCpuTime();
+
+    /** Clears the waiting to kill reason for this process. */
+    void clearWaitingToKill();
+
+    /** Adds the package to the process. */
+    void addPackage(String pkg, long versionCode);
+
+    /** Called when we are in the process on starting an activity. */
+    ProfilerInfo onStartActivity(int topProcessState);
+
+    /** App died :(...oh well */
+    void appDied();
+    void writeToProto(ProtoOutputStream proto, long fieldId);
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f0ff570..f56d8e6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4682,22 +4682,15 @@
         }
     }
 
-    @Override
-    public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
-    {
-        mDeviceLogger.log((new AudioEventLogger.StringEvent(
-                "setHearingAidDeviceConnectionState state=" + state
-                        + " addr=" + device.getAddress())).printLog(TAG));
-
-        setBluetoothHearingAidDeviceConnectionState(
-                device, state,  false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
-    }
-
     public int setBluetoothHearingAidDeviceConnectionState(
             BluetoothDevice device, int state, boolean suppressNoisyIntent,
             int musicDevice)
     {
         int delay;
+        mDeviceLogger.log((new AudioEventLogger.StringEvent(
+                "setHearingAidDeviceConnectionState state=" + state
+                            + " addr=" + device.getAddress()
+                            + " supprNoisy=" + suppressNoisyIntent)).printLog(TAG));
         synchronized (mConnectedDevices) {
             if (!suppressNoisyIntent) {
                 int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0;
@@ -5887,6 +5880,7 @@
                                    address));
         sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, null, 0);
+        setCurrentAudioRouteNameIfPossible(name);
     }
 
     private void onSendBecomingNoisyIntent() {
@@ -5908,7 +5902,7 @@
         mConnectedDevices.remove(
                 makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         // Remove A2DP routes as well
-        setCurrentAudioRouteName(null);
+        setCurrentAudioRouteNameIfPossible(null);
         if (mDockAddress == address) {
             mDockAddress = null;
         }
@@ -5975,6 +5969,10 @@
                                    address));
         sendMsg(mAudioHandler, MSG_ACCESSORY_PLUG_MEDIA_UNMUTE, SENDMSG_QUEUE,
                 AudioSystem.DEVICE_OUT_HEARING_AID, 0, null, 0);
+        sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
+                            AudioSystem.DEVICE_OUT_HEARING_AID, 0,
+                            mStreamStates[AudioSystem.STREAM_MUSIC], 0);
+        setCurrentAudioRouteNameIfPossible(name);
     }
 
     // must be called synchronized on mConnectedDevices
@@ -5984,7 +5982,7 @@
         mConnectedDevices.remove(
                 makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
         // Remove Hearing Aid routes as well
-        setCurrentAudioRouteName(null);
+        setCurrentAudioRouteNameIfPossible(null);
     }
 
     // must be called synchronized on mConnectedDevices
@@ -6029,7 +6027,6 @@
                 } else {
                     makeA2dpDeviceUnavailableNow(address);
                 }
-                setCurrentAudioRouteName(null);
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                 if (btDevice.isBluetoothDock()) {
                     // this could be a reconnection after a transient disconnection
@@ -6053,7 +6050,6 @@
                 }
                 makeA2dpDeviceAvailable(address, btDevice.getName(),
                         "onSetA2dpSinkConnectionState");
-                setCurrentAudioRouteName(btDevice.getAliasName());
             }
         }
     }
@@ -6105,25 +6101,35 @@
 
             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                 makeHearingAidDeviceUnavailable(address);
-                setCurrentAudioRouteName(null);
             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                 makeHearingAidDeviceAvailable(address, btDevice.getName(),
                         "onSetHearingAidConnectionState");
-                setCurrentAudioRouteName(btDevice.getAliasName());
             }
         }
     }
 
-    private void setCurrentAudioRouteName(String name){
+    private void setCurrentAudioRouteNameIfPossible(String name) {
         synchronized (mCurAudioRoutes) {
             if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
-                mCurAudioRoutes.bluetoothName = name;
-                sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
-                        SENDMSG_NOOP, 0, 0, null, 0);
+                if (name != null || !isCurrentDeviceConnected()) {
+                    mCurAudioRoutes.bluetoothName = name;
+                    sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+                            SENDMSG_NOOP, 0, 0, null, 0);
+                }
             }
         }
     }
 
+    private boolean isCurrentDeviceConnected() {
+        for (int i = 0; i < mConnectedDevices.size(); i++) {
+            DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
+            if (TextUtils.equals(deviceSpec.mDeviceName, mCurAudioRoutes.bluetoothName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
     {
         if (DEBUG_DEVICES) {
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index 47e85b5..15468ff 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -126,9 +126,9 @@
     public ProxyInfo getDefaultProxy() {
         // This information is already available as a world read/writable jvm property.
         synchronized (mProxyLock) {
-            final ProxyInfo ret = mGlobalProxy;
-            if ((ret == null) && mDefaultProxyEnabled) return mDefaultProxy;
-            return ret;
+            if (mGlobalProxy != null) return mGlobalProxy;
+            if (mDefaultProxyEnabled) return mDefaultProxy;
+            return null;
         }
     }
 
@@ -204,11 +204,10 @@
      *
      * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
      * to do in a "sendProxyBroadcast" method.
-     * @param proxyInfo the proxy spec, or null for no proxy.
      */
-    // TODO : make the argument NonNull final and the method private
-    public void sendProxyBroadcast(@Nullable ProxyInfo proxyInfo) {
-        if (proxyInfo == null) proxyInfo = new ProxyInfo("", 0, "");
+    public void sendProxyBroadcast() {
+        final ProxyInfo defaultProxy = getDefaultProxy();
+        final ProxyInfo proxyInfo = null != defaultProxy ? defaultProxy : new ProxyInfo("", 0, "");
         if (mPacManager.setCurrentProxyScriptUrl(proxyInfo)) return;
         if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
@@ -269,7 +268,7 @@
                 Binder.restoreCallingIdentity(token);
             }
 
-            sendProxyBroadcast(mGlobalProxy == null ? mDefaultProxy : proxyInfo);
+            sendProxyBroadcast();
         }
     }
 
@@ -296,14 +295,14 @@
                     && (!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))
                     && proxyInfo.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) {
                 mGlobalProxy = proxyInfo;
-                sendProxyBroadcast(mGlobalProxy);
+                sendProxyBroadcast();
                 return;
             }
             mDefaultProxy = proxyInfo;
 
             if (mGlobalProxy != null) return;
             if (mDefaultProxyEnabled) {
-                sendProxyBroadcast(proxyInfo);
+                sendProxyBroadcast();
             }
         }
     }
@@ -320,7 +319,7 @@
             if (mDefaultProxyEnabled != enabled) {
                 mDefaultProxyEnabled = enabled;
                 if (mGlobalProxy == null && mDefaultProxy != null) {
-                    sendProxyBroadcast(enabled ? mDefaultProxy : null);
+                    sendProxyBroadcast();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 5698fdf..5ed6263 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1615,6 +1615,15 @@
     }
 
     @Override
+    public void onDbCorruption(String tag, String message, String stacktrace) {
+        Slog.e(tag, message);
+        Slog.e(tag, "at " + stacktrace);
+
+        // TODO: Figure out a better way to report it. b/117886381
+        Slog.wtf(tag, message);
+    }
+
+    @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
             ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 8c35e27..5cbe5b9 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -20,12 +20,17 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.SystemProperties;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IntPair;
+import com.android.server.IoThread;
 
 import libcore.io.IoUtils;
 
@@ -65,10 +70,11 @@
      */
     public static synchronized SyncLogger getInstance() {
         if (sInstance == null) {
+            final String flag = SystemProperties.get("debug.synclog");
             final boolean enable =
-                    Build.IS_DEBUGGABLE
-                    || "1".equals(SystemProperties.get("debug.synclog"))
-                    || Log.isLoggable(TAG, Log.VERBOSE);
+                    (Build.IS_DEBUGGABLE
+                    || "1".equals(flag)
+                    || Log.isLoggable(TAG, Log.VERBOSE)) && !"0".equals(flag);
             if (enable) {
                 sInstance = new RotatingFileLogger();
             } else {
@@ -142,8 +148,11 @@
 
         private static final boolean DO_LOGCAT = Log.isLoggable(TAG, Log.DEBUG);
 
+        private final MyHandler mHandler;
+
         RotatingFileLogger() {
             mLogPath = new File(Environment.getDataSystemDirectory(), "syncmanager-log");
+            mHandler = new MyHandler(IoThread.get().getLooper());
         }
 
         @Override
@@ -163,8 +172,12 @@
             if (message == null) {
                 return;
             }
+            final long now = System.currentTimeMillis();
+            mHandler.log(now, message);
+        }
+
+        void logInner(long now, Object[] message) {
             synchronized (mLock) {
-                final long now = System.currentTimeMillis();
                 openLogLocked(now);
                 if (mLogWriter == null) {
                     return; // Couldn't open log file?
@@ -272,5 +285,28 @@
             } catch (IOException e) {
             }
         }
+
+        private class MyHandler extends Handler {
+            public static final int MSG_LOG_ID = 1;
+
+            MyHandler(Looper looper) {
+                super(looper);
+            }
+
+            public void log(long now, Object[] message) {
+                obtainMessage(MSG_LOG_ID, IntPair.first(now), IntPair.second(now), message)
+                        .sendToTarget();
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_LOG_ID: {
+                        logInner(IntPair.of(msg.arg1, msg.arg2), (Object[]) msg.obj);
+                        break;
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c16d3cd..b148a2f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -307,19 +307,19 @@
     private final class CecMessageBuffer {
         private List<HdmiCecMessage> mBuffer = new ArrayList<>();
 
-        public void bufferMessage(HdmiCecMessage message) {
+        public boolean bufferMessage(HdmiCecMessage message) {
             switch (message.getOpcode()) {
                 case Constants.MESSAGE_ACTIVE_SOURCE:
                     bufferActiveSource(message);
-                    break;
+                    return true;
                 case Constants.MESSAGE_IMAGE_VIEW_ON:
                 case Constants.MESSAGE_TEXT_VIEW_ON:
                     bufferImageOrTextViewOn(message);
-                    break;
+                    return true;
                     // Add here if new message that needs to buffer
                 default:
                     // Do not need to buffer messages other than above
-                    break;
+                    return false;
             }
         }
 
@@ -906,10 +906,6 @@
     @ServiceThreadOnly
     boolean handleCecCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (!mAddressAllocated) {
-            mCecMessageBuffer.bufferMessage(message);
-            return true;
-        }
         int errorCode = mMessageValidator.isValid(message);
         if (errorCode != HdmiCecMessageValidator.OK) {
             // We'll not response on the messages with the invalid source or destination
@@ -919,7 +915,12 @@
             }
             return true;
         }
-        return dispatchMessageToLocalDevice(message);
+
+        if (dispatchMessageToLocalDevice(message)) {
+            return true;
+        }
+
+        return (!mAddressAllocated) ? mCecMessageBuffer.bufferMessage(message) : false;
     }
 
     void enableAudioReturnChannel(int portId, boolean enabled) {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index d347a91..f7e871d 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -50,8 +50,7 @@
             @Override
             public void onSendCompleted(int error) {
                 if (error != SendMessageResult.SUCCESS) {
-                    tv().setSystemAudioMode(false);
-                    finish();
+                    handleSystemAudioModeStatusTimeout();
                 }
             }
         });
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 4913e8b..c20079e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -197,7 +197,7 @@
     private static native boolean nativeHasKeys(long ptr,
             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
     private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
-            InputWindowHandle inputWindowHandle, boolean monitor);
+            InputWindowHandle inputWindowHandle, int displayId);
     private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
     private static native int nativeInjectInputEvent(long ptr, InputEvent event,
@@ -473,15 +473,21 @@
     /**
      * Creates an input channel that will receive all input from the input dispatcher.
      * @param inputChannelName The input channel name.
+     * @param displayId Target display id.
      * @return The input channel.
      */
-    public InputChannel monitorInput(String inputChannelName) {
+    public InputChannel monitorInput(String inputChannelName, int displayId) {
         if (inputChannelName == null) {
             throw new IllegalArgumentException("inputChannelName must not be null.");
         }
 
+        if (displayId < Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("displayId must >= 0.");
+        }
+
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
-        nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
+        // Register channel for monitor.
+        nativeRegisterInputChannel(mPtr, inputChannels[0], null, displayId);
         inputChannels[0].dispose(); // don't need to retain the Java object reference
         return inputChannels[1];
     }
@@ -498,7 +504,8 @@
             throw new IllegalArgumentException("inputChannel must not be null.");
         }
 
-        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
+        // Register channel for normal.
+        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, Display.INVALID_DISPLAY);
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a9b0d5c..e37153e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -66,6 +66,7 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManagerInternal;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Binder;
@@ -103,6 +104,8 @@
 import android.util.Slog;
 import android.util.Xml;
 import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.LayoutInflater;
@@ -110,10 +113,12 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionInspector;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
@@ -130,6 +135,9 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.HandlerCaller;
@@ -145,7 +153,6 @@
 import com.android.internal.view.IInputMethodSession;
 import com.android.internal.view.IInputSessionCallback;
 import com.android.internal.view.InputBindResult;
-import com.android.internal.view.InputMethodClient;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -393,6 +400,7 @@
     };
 
     private void restoreNonVrImeFromSettingsNoCheck() {
+        mIsVrImeStarted = false;
         // switch back to non-VR InputMethod from settings.
         synchronized (mMethodMap) {
             final String lastInputId = mSettings.getSelectedInputMethod();
@@ -421,6 +429,7 @@
         final IInputContext inputContext;
         final int uid;
         final int pid;
+        final int selfReportedDisplayId;
         final InputBinding binding;
         final ClientDeathRecipient clientDeathRecipient;
 
@@ -430,16 +439,18 @@
         @Override
         public String toString() {
             return "ClientState{" + Integer.toHexString(
-                    System.identityHashCode(this)) + " uid " + uid
-                    + " pid " + pid + "}";
+                    System.identityHashCode(this)) + " uid=" + uid
+                    + " pid=" + pid + " displayId=" + selfReportedDisplayId + "}";
         }
 
         ClientState(IInputMethodClient _client, IInputContext _inputContext,
-                int _uid, int _pid, ClientDeathRecipient _clientDeathRecipient) {
+                int _uid, int _pid, int _selfReportedDisplayId,
+                ClientDeathRecipient _clientDeathRecipient) {
             client = _client;
             inputContext = _inputContext;
             uid = _uid;
             pid = _pid;
+            selfReportedDisplayId = _selfReportedDisplayId;
             binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
             clientDeathRecipient = _clientDeathRecipient;
         }
@@ -498,6 +509,7 @@
      *
      * @see #mCurFocusedWindow
      */
+    @SoftInputModeFlags
     int mCurFocusedWindowSoftInputMode;
 
     /**
@@ -515,6 +527,7 @@
      *
      * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
      */
+    @MissingMethodFlags
     int mCurInputContextMissingMethods;
 
     /**
@@ -592,6 +605,11 @@
      */
     int mCurTokenDisplayId = INVALID_DISPLAY;
 
+    final ImeDisplayValidator mImeDisplayValidator;
+
+    /** True if VR IME started by {@link #startVrInputMethodNoCheck}. */
+    boolean mIsVrImeStarted;
+
     /**
      * If non-null, this is the input method service we are currently connected
      * to.
@@ -688,20 +706,21 @@
         final IBinder mImeToken;
         @NonNull
         final String mImeId;
-        // @InputMethodClient.StartInputReason
+        @StartInputReason
         final int mStartInputReason;
         final boolean mRestarting;
         @Nullable
         final IBinder mTargetWindow;
         @NonNull
         final EditorInfo mEditorInfo;
+        @SoftInputModeFlags
         final int mTargetWindowSoftInputMode;
         final int mClientBindSequenceNumber;
 
         StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId,
-                /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting,
+                @StartInputReason int startInputReason, boolean restarting,
                 @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo,
-                int targetWindowSoftInputMode, int clientBindSequenceNumber) {
+                @SoftInputModeFlags int targetWindowSoftInputMode, int clientBindSequenceNumber) {
             mSequenceNumber = sSequenceNumber.getAndIncrement();
             mTimestamp = SystemClock.uptimeMillis();
             mWallTime = System.currentTimeMillis();
@@ -769,13 +788,14 @@
             String mImeTokenString;
             @NonNull
             String mImeId;
-            /* @InputMethodClient.StartInputReason */
+            @StartInputReason
             int mStartInputReason;
             boolean mRestarting;
             @NonNull
             String mTargetWindowString;
             @NonNull
             EditorInfo mEditorInfo;
+            @SoftInputModeFlags
             int mTargetWindowSoftInputMode;
             int mClientBindSequenceNumber;
 
@@ -832,7 +852,7 @@
                 pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
                         + " (timestamp=" + entry.mTimestamp + ")"
                         + " reason="
-                        + InputMethodClient.getStartInputReason(entry.mStartInputReason)
+                        + InputMethodDebug.startInputReasonToString(entry.mStartInputReason)
                         + " restarting=" + entry.mRestarting);
 
                 pw.print(prefix);
@@ -844,7 +864,7 @@
                         + " clientBindSeq=" + entry.mClientBindSequenceNumber);
 
                 pw.print(prefix);
-                pw.println(" softInputMode=" + InputMethodClient.softInputModeToString(
+                pw.println(" softInputMode=" + InputMethodDebug.softInputModeToString(
                                 entry.mTargetWindowSoftInputMode));
 
                 pw.print(prefix);
@@ -990,6 +1010,7 @@
                     // set this is as current inputMethod without updating settings.
                     setInputMethodEnabledLocked(info.getId(), true);
                     setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID);
+                    mIsVrImeStarted = true;
                     break;
                 }
             }
@@ -1386,6 +1407,13 @@
         mIWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService(Context.WINDOW_SERVICE));
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+        final DisplayManagerInternal displayManagerInternal = LocalServices.getService(
+                DisplayManagerInternal.class);
+        mImeDisplayValidator = (displayId) -> {
+            final DisplayInfo displayInfo = displayManagerInternal.getDisplayInfo(displayId);
+            return displayInfo != null
+                    && (displayInfo.flags & Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0;
+        };
         mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
             @Override
             public void executeMessage(Message msg) {
@@ -1497,7 +1525,7 @@
         // TODO: Is it really possible that switchUserLocked() happens before system ready?
         if (mSystemReady) {
             hideCurrentInputLocked(0, null);
-            resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_USER);
+            resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
             buildInputMethodListLocked(initialUserSwitch);
             if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
                 // This is the first time of the user switch and
@@ -1505,11 +1533,6 @@
                 resetDefaultImeLocked(mContext);
             }
             updateFromSettingsLocked(true);
-            try {
-                startInputInnerLocked();
-            } catch (RuntimeException e) {
-                Slog.w(TAG, "Unexpected exception", e);
-            }
         }
 
         if (initialUserSwitch) {
@@ -1585,12 +1608,6 @@
                 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
                         mSettings.getEnabledInputMethodListLocked(), currentUserId,
                         mContext.getBasePackageName());
-
-                try {
-                    startInputInnerLocked();
-                } catch (RuntimeException e) {
-                    Slog.w(TAG, "Unexpected exception", e);
-                }
             }
         }
     }
@@ -1738,22 +1755,28 @@
      * {@link InputMethodManagerService}.
      *
      * <p>As a general principle, IPCs from the application process that take
-     * {@link InputMethodClient} will be rejected without this step.</p>
+     * {@link IInputMethodClient} will be rejected without this step.</p>
      *
      * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
      *               of {@link android.view.inputmethod.InputMethodManager} that runs on the client
      *               process
      * @param inputContext communication channel for the dummy
      *                     {@link android.view.inputmethod.InputConnection}
+     * @param selfReportedDisplayId self-reported display ID to which the client is associated.
+     *                              Whether the client is still allowed to access to this display
+     *                              or not needs to be evaluated every time the client interacts
+     *                              with the display
      */
     @Override
-    public void addClient(IInputMethodClient client, IInputContext inputContext) {
+    public void addClient(IInputMethodClient client, IInputContext inputContext,
+            int selfReportedDisplayId) {
         final int callerUid = Binder.getCallingUid();
         final int callerPid = Binder.getCallingPid();
         synchronized (mMethodMap) {
             // TODO: Optimize this linear search.
             for (ClientState state : mClients.values()) {
-                if (state.uid == callerUid && state.pid == callerPid) {
+                if (state.uid == callerUid && state.pid == callerPid
+                        && state.selfReportedDisplayId == selfReportedDisplayId) {
                     throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
                             + " is already registered");
                 }
@@ -1764,8 +1787,18 @@
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
-            mClients.put(client.asBinder(),
-                    new ClientState(client, inputContext, callerUid, callerPid, deathRecipient));
+            // We cannot fully avoid race conditions where the client UID already lost the access to
+            // the given self-reported display ID, even if the client is not maliciously reporting
+            // a fake display ID. Unconditionally returning SecurityException just because the
+            // client doesn't pass display ID verification can cause many test failures hence not an
+            // option right now.  At the same time
+            //    context.getSystemService(InputMethodManager.class)
+            // is expected to return a valid non-null instance at any time if we do not choose to
+            // have the client crash.  Thus we do not verify the display ID at all here.  Instead we
+            // later check the display ID every time the client needs to interact with the specified
+            // display.
+            mClients.put(client.asBinder(), new ClientState(client, inputContext, callerUid,
+                    callerPid, selfReportedDisplayId, deathRecipient));
         }
     }
 
@@ -1801,8 +1834,7 @@
          }
     }
 
-    void unbindCurrentClientLocked(
-            /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+    void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
             if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
                     + mCurClient.client.asBinder());
@@ -1848,8 +1880,7 @@
 
     @GuardedBy("mMethodMap")
     @NonNull
-    InputBindResult attachNewInputLocked(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
+    InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
         if (!mBoundToMethod) {
             executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                     MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
@@ -1879,14 +1910,21 @@
     @GuardedBy("mMethodMap")
     @NonNull
     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */ final int missingMethods,
-            @NonNull EditorInfo attribute, int controlFlags,
-            /* @InputMethodClient.StartInputReason */ final int startInputReason) {
+            @MissingMethodFlags int missingMethods, @NonNull EditorInfo attribute, int controlFlags,
+            @StartInputReason int startInputReason) {
         // If no method is currently selected, do nothing.
         if (mCurMethodId == null) {
             return InputBindResult.NO_IME;
         }
 
+        if (!mSystemReady) {
+            // If the system is not yet ready, we shouldn't be running third
+            // party code.
+            return new InputBindResult(
+                    InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
+                    null, null, mCurMethodId, mCurSeq);
+        }
+
         if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
                 attribute.packageName)) {
             Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
@@ -1894,12 +1932,21 @@
             return InputBindResult.INVALID_PACKAGE_NAME;
         }
 
+        if (!mWindowManagerInternal.isUidAllowedOnDisplay(cs.selfReportedDisplayId, cs.uid)) {
+            // Wait, the client no longer has access to the display.
+            return InputBindResult.INVALID_DISPLAY_ID;
+        }
+        // Compute the final shown display ID with validated cs.selfReportedDisplayId for this
+        // session & other conditions.
+        final int displayIdToShowIme = computeImeDisplayIdForTarget(
+                cs.selfReportedDisplayId, mIsVrImeStarted, mImeDisplayValidator);
+
         if (mCurClient != cs) {
             // Was the keyguard locked when switching over to the new client?
             mCurClientInKeyguard = isKeyguardLocked();
             // If the client is changing, we need to switch over to the new
             // one.
-            unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_CLIENT);
+            unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
             if (DEBUG) Slog.v(TAG, "switching to client: client="
                     + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
 
@@ -1918,9 +1965,10 @@
         mCurAttribute = attribute;
 
         // Check if the input method is changing.
-        final int displayId = mWindowManagerInternal.getDisplayIdForWindow(
-                mCurFocusedWindow);
-        if (mCurId != null && mCurId.equals(mCurMethodId) && displayId == mCurTokenDisplayId) {
+        // We expect the caller has already verified that the client is allowed to access this
+        // display ID.
+        if (mCurId != null && mCurId.equals(mCurMethodId)
+                && displayIdToShowIme == mCurTokenDisplayId) {
             if (cs.curSession != null) {
                 // Fast case: if we are already connected to the input method,
                 // then just return it.
@@ -1954,23 +2002,6 @@
             }
         }
 
-        return startInputInnerLocked();
-    }
-
-    @GuardedBy("mMethodMap")
-    InputBindResult startInputInnerLocked() {
-        if (mCurMethodId == null) {
-            return InputBindResult.NO_IME;
-        }
-
-        if (!mSystemReady) {
-            // If the system is not yet ready, we shouldn't be running third
-            // party code.
-            return new InputBindResult(
-                    InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
-                    null, null, mCurMethodId, mCurSeq);
-        }
-
         InputMethodInfo info = mMethodMap.get(mCurMethodId);
         if (info == null) {
             throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
@@ -1984,14 +2015,13 @@
                 com.android.internal.R.string.input_method_binding_label);
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
-        final int displayId = mWindowManagerInternal.getDisplayIdForWindow(mCurFocusedWindow);
-        mCurTokenDisplayId = (displayId != INVALID_DISPLAY) ? displayId : DEFAULT_DISPLAY;
 
         if (bindCurrentInputMethodServiceLocked(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
             mLastBindTime = SystemClock.uptimeMillis();
             mHaveConnection = true;
             mCurId = info.getId();
             mCurToken = new Binder();
+            mCurTokenDisplayId = displayIdToShowIme;
             try {
                 if (DEBUG) {
                     Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
@@ -2009,8 +2039,33 @@
         return InputBindResult.IME_NOT_CONNECTED;
     }
 
-    @Override
-    public void finishInput(IInputMethodClient client) {
+    @FunctionalInterface
+    interface ImeDisplayValidator {
+        boolean displayCanShowIme(int displayId);
+    }
+
+    /**
+     * Find the display where the IME should be shown.
+     *
+     * @param displayId the ID of the display where the IME client target is.
+     * @param isVrImeStarted {@code true} if VR IME started, {@code false} otherwise.
+     * @param checker instance of {@link ImeDisplayValidator} which is used for
+     *                checking display config to adjust the final target display.
+     * @return The ID of the display where the IME should be shown.
+     */
+    static int computeImeDisplayIdForTarget(int displayId, boolean isVrImeStarted,
+            @NonNull ImeDisplayValidator checker) {
+        // For VR IME, we always show in default display.
+        if (isVrImeStarted) {
+            return DEFAULT_DISPLAY;
+        }
+        if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
+            // We always assume that the default display id suitable to show the IME window.
+            return DEFAULT_DISPLAY;
+        }
+        // Show IME in default display when the display with IME target doesn't support system
+        // decorations.
+        return checker.displayCanShowIme(displayId) ? displayId : DEFAULT_DISPLAY;
     }
 
     @Override
@@ -2045,7 +2100,7 @@
                     mCurClient.curSession = new SessionState(mCurClient,
                             method, session, channel);
                     InputBindResult res = attachNewInputLocked(
-                            InputMethodClient.START_INPUT_REASON_SESSION_CREATED_BY_IME, true);
+                            StartInputReason.SESSION_CREATED_BY_IME, true);
                     if (res.method != null) {
                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
                                 MSG_BIND_CLIENT, mCurClient.client, res));
@@ -2087,8 +2142,7 @@
         clearCurMethodLocked();
     }
 
-    void resetCurrentMethodAndClient(
-            /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
+    void resetCurrentMethodAndClient(@UnbindReason int unbindClientReason) {
         mCurMethodId = null;
         unbindCurrentMethodLocked();
         unbindCurrentClientLocked(unbindClientReason);
@@ -2165,7 +2219,7 @@
                 mLastBindTime = SystemClock.uptimeMillis();
                 mShowRequested = mInputShown;
                 mInputShown = false;
-                unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_DISCONNECT_IME);
+                unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
             }
         }
     }
@@ -2476,12 +2530,12 @@
                 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
-                resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
+                resetCurrentMethodAndClient(UnbindReason.SWITCH_IME_FAILED);
             }
             mShortcutInputMethodsAndSubtypes.clear();
         } else {
             // There is no longer an input method set, so stop any current one.
-            resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
+            resetCurrentMethodAndClient(UnbindReason.NO_IME);
         }
         // Here is not the perfect place to reset the switching controller. Ideally
         // mSwitchingController and mSettings should be able to share the same state.
@@ -2559,7 +2613,7 @@
                 intent.putExtra("input_method_id", id);
                 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
             }
-            unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
+            unbindCurrentClientLocked(UnbindReason.SWITCH_IME);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -2584,7 +2638,8 @@
                     if (cs == null) {
                         throw new IllegalArgumentException("unknown client " + client.asBinder());
                     }
-                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
+                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
+                            cs.selfReportedDisplayId)) {
                         Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
                         return false;
                     }
@@ -2668,7 +2723,8 @@
                     if (cs == null) {
                         throw new IllegalArgumentException("unknown client " + client.asBinder());
                     }
-                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
+                    if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
+                            cs.selfReportedDisplayId)) {
                         if (DEBUG) {
                             Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client);
                         }
@@ -2731,11 +2787,10 @@
     @NonNull
     @Override
     public InputBindResult startInputOrWindowGainedFocus(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
-            int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */ final int missingMethods,
-            int unverifiedTargetSdkVersion) {
+            @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+            int controlFlags, @SoftInputModeFlags int softInputMode, int windowFlags,
+            @Nullable EditorInfo attribute, IInputContext inputContext,
+            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
@@ -2746,7 +2801,7 @@
         if (result == null) {
             // This must never happen, but just in case.
             Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
-                    + InputMethodClient.getStartInputReason(startInputReason)
+                    + InputMethodDebug.startInputReasonToString(startInputReason)
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
                     + " editorInfo=" + attribute);
             return InputBindResult.NULL;
@@ -2756,27 +2811,27 @@
 
     @NonNull
     private InputBindResult startInputOrWindowGainedFocusInternal(
-            /* @InputMethodClient.StartInputReason */ final int startInputReason,
-            IInputMethodClient client, @NonNull IBinder windowToken, int controlFlags,
-            /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+            @StartInputReason int startInputReason, IInputMethodClient client,
+            @NonNull IBinder windowToken, int controlFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, EditorInfo attribute, IInputContext inputContext,
-            /* @InputConnectionInspector.missingMethods */  final int missingMethods,
-            int unverifiedTargetSdkVersion) {
+            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
         // Needs to check the validity before clearing calling identity
         final boolean calledFromValidUser = calledFromValidUser();
         InputBindResult res = null;
         long ident = Binder.clearCallingIdentity();
         try {
+            final int windowDisplayId =
+                    mWindowManagerInternal.getDisplayIdForWindow(windowToken);
             synchronized (mMethodMap) {
                 if (DEBUG) Slog.v(TAG, "startInputOrWindowGainedFocusInternal: reason="
-                        + InputMethodClient.getStartInputReason(startInputReason)
+                        + InputMethodDebug.startInputReasonToString(startInputReason)
                         + " client=" + client.asBinder()
                         + " inputContext=" + inputContext
                         + " missingMethods="
                         + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
                         + " attribute=" + attribute
                         + " controlFlags=#" + Integer.toHexString(controlFlags)
-                        + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
+                        + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                         + " windowFlags=#" + Integer.toHexString(windowFlags)
                         + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
 
@@ -2785,8 +2840,15 @@
                     throw new IllegalArgumentException("unknown client "
                             + client.asBinder());
                 }
+                if (cs.selfReportedDisplayId != windowDisplayId) {
+                    Slog.e(TAG, "startInputOrWindowGainedFocusInternal: display ID mismatch."
+                            + " from client:" + cs.selfReportedDisplayId
+                            + " from window:" + windowDisplayId);
+                    return InputBindResult.DISPLAY_ID_MISMATCH;
+                }
 
-                if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid)) {
+                if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid,
+                        cs.selfReportedDisplayId)) {
                     // Check with the window manager to make sure this client actually
                     // has a window with focus.  If not, reject.  This is thread safe
                     // because if the focus changes some time before or after, the
@@ -2858,9 +2920,9 @@
                                 // If focused display changed, we should unbind current method
                                 // to make app window in previous display relayout after Ime
                                 // window token removed.
-                                final int newFocusDisplayId =
-                                        mWindowManagerInternal.getDisplayIdForWindow(windowToken);
-                                if (newFocusDisplayId != mCurTokenDisplayId) {
+                                // Note that we can trust client's display ID as long as it matches
+                                // to the display ID obtained from the window.
+                                if (cs.selfReportedDisplayId != mCurTokenDisplayId) {
                                     unbindCurrentMethodLocked();
                                 }
                             }
@@ -2958,6 +3020,7 @@
     }
 
     private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
+        // TODO(yukawa): multi-display support.
         final int uid = Binder.getCallingUid();
         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
             return true;
@@ -3034,6 +3097,7 @@
     @Override
     public void showInputMethodAndSubtypeEnablerFromClient(
             IInputMethodClient client, String inputMethodId) {
+        // TODO(yukawa): Should we verify the display ID?
         if (!calledFromValidUser()) {
             return;
         }
@@ -3229,6 +3293,7 @@
      */
     @Override
     public int getInputMethodWindowVisibleHeight() {
+        // TODO(yukawa): Should we verify the display ID?
         return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
     }
 
@@ -4564,7 +4629,7 @@
             p.println("  mCurClient=" + client + " mCurSeq=" + mCurSeq);
             p.println("  mCurFocusedWindow=" + mCurFocusedWindow
                     + " softInputMode=" +
-                    InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode)
+                    InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
                     + " client=" + mCurFocusedWindowClient);
             focusedWindowClient = mCurFocusedWindowClient;
             p.println("  mCurId=" + mCurId + " mHaveConnection=" + mHaveConnection
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 0c66c5b..6989c33 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -55,6 +55,8 @@
  * Each app can have a different default networks or different connectivity
  * status due to user-requested network policies, so we need to check
  * constraints on a per-UID basis.
+ *
+ * Test: atest com.android.server.job.controllers.ConnectivityControllerTest
  */
 public final class ConnectivityController extends StateController implements
         ConnectivityManager.OnNetworkActiveListener {
@@ -65,8 +67,9 @@
     private final ConnectivityManager mConnManager;
     private final NetworkPolicyManager mNetPolicyManager;
 
+    /** List of tracked jobs keyed by source UID. */
     @GuardedBy("mLock")
-    private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
+    private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
 
     public ConnectivityController(JobSchedulerService service) {
         super(service);
@@ -87,7 +90,12 @@
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
         if (jobStatus.hasConnectivityConstraint()) {
             updateConstraintsSatisfied(jobStatus);
-            mTrackedJobs.add(jobStatus);
+            ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
+            if (jobs == null) {
+                jobs = new ArraySet<>();
+                mTrackedJobs.put(jobStatus.getSourceUid(), jobs);
+            }
+            jobs.add(jobStatus);
             jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
         }
     }
@@ -97,7 +105,10 @@
     public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
             boolean forUpdate) {
         if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) {
-            mTrackedJobs.remove(jobStatus);
+            ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUid());
+            if (jobs != null) {
+                jobs.remove(jobStatus);
+            }
         }
     }
 
@@ -235,47 +246,26 @@
     /**
      * Update any jobs tracked by this controller that match given filters.
      *
-     * @param filterUid only update jobs belonging to this UID, or {@code -1} to
-     *            update all tracked jobs.
+     * @param filterUid     only update jobs belonging to this UID, or {@code -1} to
+     *                      update all tracked jobs.
      * @param filterNetwork only update jobs that would use this
-     *            {@link Network}, or {@code null} to update all tracked jobs.
+     *                      {@link Network}, or {@code null} to update all tracked jobs.
      */
     private void updateTrackedJobs(int filterUid, Network filterNetwork) {
         synchronized (mLock) {
             // Since this is a really hot codepath, temporarily cache any
             // answers that we get from ConnectivityManager.
-            final SparseArray<Network> uidToNetwork = new SparseArray<>();
             final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>();
 
             boolean changed = false;
-            for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
-                final JobStatus js = mTrackedJobs.valueAt(i);
-                final int uid = js.getSourceUid();
-
-                final boolean uidMatch = (filterUid == -1 || filterUid == uid);
-                if (uidMatch) {
-                    Network network = uidToNetwork.get(uid);
-                    if (network == null) {
-                        network = mConnManager.getActiveNetworkForUid(uid);
-                        uidToNetwork.put(uid, network);
-                    }
-
-                    // Update either when we have a network match, or when the
-                    // job hasn't yet been evaluated against the currently
-                    // active network; typically when we just lost a network.
-                    final boolean networkMatch = (filterNetwork == null
-                            || Objects.equals(filterNetwork, network));
-                    final boolean forceUpdate = !Objects.equals(js.network, network);
-                    if (networkMatch || forceUpdate) {
-                        final int netId = network != null ? network.netId : -1;
-                        NetworkCapabilities capabilities = networkToCapabilities.get(netId);
-                        if (capabilities == null) {
-                            capabilities = mConnManager.getNetworkCapabilities(network);
-                            networkToCapabilities.put(netId, capabilities);
-                        }
-                        changed |= updateConstraintsSatisfied(js, network, capabilities);
-                    }
+            if (filterUid == -1) {
+                for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
+                    changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i),
+                            filterNetwork, networkToCapabilities);
                 }
+            } else {
+                changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid),
+                        filterNetwork, networkToCapabilities);
             }
             if (changed) {
                 mStateChangedListener.onControllerStateChanged();
@@ -283,6 +273,36 @@
         }
     }
 
+    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork,
+            SparseArray<NetworkCapabilities> networkToCapabilities) {
+        if (jobs == null || jobs.size() == 0) {
+            return false;
+        }
+
+        final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid());
+        final int netId = network != null ? network.netId : -1;
+        NetworkCapabilities capabilities = networkToCapabilities.get(netId);
+        if (capabilities == null) {
+            capabilities = mConnManager.getNetworkCapabilities(network);
+            networkToCapabilities.put(netId, capabilities);
+        }
+        final boolean networkMatch = (filterNetwork == null
+                || Objects.equals(filterNetwork, network));
+
+        boolean changed = false;
+        for (int i = jobs.size() - 1; i >= 0; i--) {
+            final JobStatus js = jobs.valueAt(i);
+
+            // Update either when we have a network match, or when the
+            // job hasn't yet been evaluated against the currently
+            // active network; typically when we just lost a network.
+            if (networkMatch || !Objects.equals(js.network, network)) {
+                changed |= updateConstraintsSatisfied(js, network, capabilities);
+            }
+        }
+        return changed;
+    }
+
     /**
      * We know the network has just come up. We want to run any jobs that are ready.
      */
@@ -290,12 +310,15 @@
     public void onNetworkActive() {
         synchronized (mLock) {
             for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
-                final JobStatus js = mTrackedJobs.valueAt(i);
-                if (js.isReady()) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Running " + js + " due to network activity.");
+                final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
+                for (int j = jobs.size() - 1; j >= 0; j--) {
+                    final JobStatus js = jobs.valueAt(j);
+                    if (js.isReady()) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Running " + js + " due to network activity.");
+                        }
+                        mStateChangedListener.onRunJobNow(js);
                     }
-                    mStateChangedListener.onRunJobNow(js);
                 }
             }
         }
@@ -334,8 +357,12 @@
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
         for (int i = 0; i < mTrackedJobs.size(); i++) {
-            final JobStatus js = mTrackedJobs.valueAt(i);
-            if (predicate.test(js)) {
+            final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
+            for (int j = 0; j < jobs.size(); j++) {
+                final JobStatus js = jobs.valueAt(j);
+                if (!predicate.test(js)) {
+                    continue;
+                }
                 pw.print("#");
                 js.printUniqueId(pw);
                 pw.print(" from ");
@@ -355,20 +382,26 @@
         final long mToken = proto.start(StateControllerProto.CONNECTIVITY);
 
         for (int i = 0; i < mTrackedJobs.size(); i++) {
-            final JobStatus js = mTrackedJobs.valueAt(i);
-            if (!predicate.test(js)) {
-                continue;
+            final ArraySet<JobStatus> jobs = mTrackedJobs.valueAt(i);
+            for (int j = 0; j < jobs.size(); j++) {
+                final JobStatus js = jobs.valueAt(j);
+                if (!predicate.test(js)) {
+                    continue;
+                }
+                final long jsToken = proto.start(
+                        StateControllerProto.ConnectivityController.TRACKED_JOBS);
+                js.writeToShortProto(proto,
+                        StateControllerProto.ConnectivityController.TrackedJob.INFO);
+                proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
+                        js.getSourceUid());
+                NetworkRequest rn = js.getJob().getRequiredNetwork();
+                if (rn != null) {
+                    rn.writeToProto(proto,
+                            StateControllerProto.ConnectivityController.TrackedJob
+                                    .REQUIRED_NETWORK);
+                }
+                proto.end(jsToken);
             }
-            final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS);
-            js.writeToShortProto(proto, StateControllerProto.ConnectivityController.TrackedJob.INFO);
-            proto.write(StateControllerProto.ConnectivityController.TrackedJob.SOURCE_UID,
-                    js.getSourceUid());
-            NetworkRequest rn = js.getJob().getRequiredNetwork();
-            if (rn != null) {
-                rn.writeToProto(proto,
-                        StateControllerProto.ConnectivityController.TrackedJob.REQUIRED_NETWORK);
-            }
-            proto.end(jsToken);
         }
 
         proto.end(mToken);
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 3f8941d..4ece538d 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -57,6 +57,8 @@
  * This isn't strictly necessary because each controller is only interested in a specific field,
  * and the receivers that are listening for global state change will all run on the main looper,
  * but we don't enforce that so this is safer.
+ *
+ * Test: atest com.android.server.job.controllers.JobStatusTest
  * @hide
  */
 public final class JobStatus {
@@ -154,7 +156,9 @@
 
     // Constraints.
     final int requiredConstraints;
+    private final int mRequiredConstraintsOfInterest;
     int satisfiedConstraints = 0;
+    private int mSatisfiedConstraintsOfInterest = 0;
 
     // Set to true if doze constraint was satisfied due to app being whitelisted.
     public boolean dozeWhitelisted;
@@ -265,6 +269,28 @@
 
     private long totalNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
 
+    /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
+    /////// states change.
+
+    /**
+     * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
+     * should only run if its constraints are satisfied.
+     * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
+     */
+    private boolean mReadyDeadlineSatisfied;
+
+    /**
+     * The device isn't Dozing or this job will be in the foreground. This implicit constraint must
+     * be satisfied.
+     */
+    private boolean mReadyNotDozing;
+
+    /**
+     * The job is not restricted from running in the background (due to Battery Saver). This
+     * implicit constraint must be satisfied.
+     */
+    private boolean mReadyNotRestrictedInBg;
+
     /** Provide a handle to the service that this job will be run on. */
     public int getServiceToken() {
         return callingUid;
@@ -349,6 +375,8 @@
             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
         }
         this.requiredConstraints = requiredConstraints;
+        mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
+        mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
 
         mLastSuccessfulRunTime = lastSuccessfulRunTime;
         mLastFailedRunTime = lastFailedRunTime;
@@ -865,7 +893,12 @@
     }
 
     boolean setDeadlineConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
+        if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
+            return true;
+        }
+        return false;
     }
 
     boolean setIdleConstraintSatisfied(boolean state) {
@@ -882,11 +915,21 @@
 
     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
         dozeWhitelisted = whitelisted;
-        return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
+        if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
+            return true;
+        }
+        return false;
     }
 
     boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
+        if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
+            // The constraint was changed. Update the ready flag.
+            mReadyNotRestrictedInBg = state;
+            return true;
+        }
+        return false;
     }
 
     boolean setUidActive(final boolean newActiveState) {
@@ -903,6 +946,7 @@
             return false;
         }
         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
+        mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
         return true;
     }
 
@@ -933,24 +977,15 @@
     /**
      * @return Whether or not this job is ready to run, based on its requirements. This is true if
      * the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
-     * TODO: This function is called a *lot*.  We should probably just have it check an
-     * already-computed boolean, which we updated whenever we see one of the states it depends
-     * on here change.
      */
     public boolean isReady() {
         // Deadline constraint trumps other constraints (except for periodic jobs where deadline
         // is an implementation detail. A periodic job should only run if its constraints are
         // satisfied).
-        // AppNotIdle implicit constraint must be satisfied
         // DeviceNotDozing implicit constraint must be satisfied
         // NotRestrictedInBackground implicit constraint must be satisfied
-        final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
-                && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
-        final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
-                || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
-        final boolean notRestrictedInBg =
-                (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
-        return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
+        return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
+                || isConstraintsSatisfied());
     }
 
     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -971,15 +1006,13 @@
             return true;
         }
 
-        final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
-
-        int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+        int sat = mSatisfiedConstraintsOfInterest;
         if (overrideState == OVERRIDE_SOFT) {
             // override: pretend all 'soft' requirements are satisfied
             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
         }
 
-        return (sat & req) == req;
+        return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
     }
 
     public boolean matches(int uid, int jobId) {
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index b29b7cf..1d0ab8f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -16,10 +16,12 @@
 
 package com.android.server.location;
 
+import android.app.PendingIntent;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
 import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubInfo;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
@@ -57,9 +59,9 @@
     private final ContextHubClientManager mClientManager;
 
     /*
-     * The ID of the hub that this client is attached to.
+     * The object describing the hub that this client is attached to.
      */
-    private final int mAttachedContextHubId;
+    private final ContextHubInfo mAttachedContextHubInfo;
 
     /*
      * The host end point ID of this client.
@@ -85,11 +87,12 @@
 
     /* package */ ContextHubClientBroker(
             Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
-            int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+            ContextHubInfo contextHubInfo, short hostEndPointId,
+            IContextHubClientCallback callback) {
         mContext = context;
         mContextHubProxy = contextHubProxy;
         mClientManager = clientManager;
-        mAttachedContextHubId = contextHubId;
+        mAttachedContextHubInfo = contextHubInfo;
         mHostEndPointId = hostEndPointId;
         mCallbackInterface = callback;
     }
@@ -119,11 +122,12 @@
             ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
                     mHostEndPointId, message);
 
+            int contextHubId = mAttachedContextHubInfo.getId();
             try {
-                result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+                result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
-                        + mAttachedContextHubId + ")", e);
+                        + contextHubId + ")", e);
                 result = Result.UNKNOWN_FAILURE;
             }
         } else {
@@ -135,6 +139,27 @@
     }
 
     /**
+     * @param intent the intent to register
+     * @param nanoAppId the ID of the nanoapp to send events for
+     * @return true on success, false otherwise
+     */
+    @Override
+    public boolean registerIntent(PendingIntent intent, long nanoAppId) {
+        // TODO: Implement this
+        return false;
+    }
+
+    /**
+     * @param intent the intent to unregister
+     * @return true on success, false otherwise
+     */
+    @Override
+    public boolean unregisterIntent(PendingIntent intent) {
+        // TODO: Implement this
+        return false;
+    }
+
+    /**
      * Closes the connection for this client with the service.
      */
     @Override
@@ -156,7 +181,7 @@
      * @return the ID of the context hub this client is attached to
      */
     /* package */ int getAttachedContextHubId() {
-        return mAttachedContextHubId;
+        return mAttachedContextHubInfo.getId();
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 4243f02..eda8c6f 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -19,13 +19,13 @@
 import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
 import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.ContextHubInfo;
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.NanoAppMessage;
 import android.os.RemoteException;
 import android.util.Log;
 
-import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
@@ -80,15 +80,15 @@
      * Registers a new client with the service.
      *
      * @param clientCallback the callback interface of the client to register
-     * @param contextHubId   the ID of the hub this client is attached to
+     * @param contextHubInfo the object describing the hub this client is attached to
      *
      * @return the client interface
      *
      * @throws IllegalStateException if max number of clients have already registered
      */
     /* package */ IContextHubClient registerClient(
-            IContextHubClientCallback clientCallback, int contextHubId) {
-        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+            IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+        ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
 
         try {
             broker.attachDeathRecipient();
@@ -183,14 +183,14 @@
      * manager.
      *
      * @param clientCallback the callback interface of the client to register
-     * @param contextHubId   the ID of the hub this client is attached to
+     * @param contextHubInfo the object describing the hub this client is attached to
      *
      * @return the ContextHubClientBroker object
      *
      * @throws IllegalStateException if max number of clients have already registered
      */
     private synchronized ContextHubClientBroker createNewClientBroker(
-            IContextHubClientCallback clientCallback, int contextHubId) {
+            IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
         if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
             throw new IllegalStateException("Could not register client - max limit exceeded");
         }
@@ -198,10 +198,11 @@
         ContextHubClientBroker broker = null;
         int id = mNextHostEndpointId;
         for (int i = 0; i <= MAX_CLIENT_ID; i++) {
-            if (!mHostEndPointIdToClientMap.containsKey((short)id)) {
+            if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
                 broker = new ContextHubClientBroker(
-                        mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
-                mHostEndPointIdToClientMap.put((short)id, broker);
+                        mContext, mContextHubProxy, this, contextHubInfo, (short) id,
+                        clientCallback);
+                mHostEndPointIdToClientMap.put((short) id, broker);
                 mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
                 break;
             }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 27509de..96e9337 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -170,8 +170,9 @@
 
         HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
         for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+            ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
             IContextHubClient client = mClientManager.registerClient(
-                    createDefaultClientCallback(contextHubId), contextHubId);
+                    createDefaultClientCallback(contextHubId), contextHubInfo);
             defaultClientMap.put(contextHubId, client);
 
             try {
@@ -623,7 +624,8 @@
             throw new NullPointerException("Cannot register client with null callback");
         }
 
-        return mClientManager.registerClient(clientCallback, contextHubId);
+        ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
+        return mClientManager.registerClient(clientCallback, contextHubInfo);
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/MediaUpdateService.java b/services/core/java/com/android/server/media/MediaUpdateService.java
index af06d15..7304f07 100644
--- a/services/core/java/com/android/server/media/MediaUpdateService.java
+++ b/services/core/java/com/android/server/media/MediaUpdateService.java
@@ -22,7 +22,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.media.IMediaExtractorUpdateService;
+import android.media.IMediaUpdateService;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Handler;
@@ -34,6 +34,7 @@
 import android.util.Log;
 import android.util.Slog;
 import com.android.server.SystemService;
+import java.util.HashMap;
 
 /** This class provides a system service that manages media framework updates. */
 public class MediaUpdateService extends SystemService {
@@ -42,34 +43,40 @@
     private static final String MEDIA_UPDATE_PACKAGE_NAME =
             SystemProperties.get("ro.mediacomponents.package");
     private static final String EXTRACTOR_UPDATE_SERVICE_NAME = "media.extractor.update";
-
-    private IMediaExtractorUpdateService mMediaExtractorUpdateService;
-    final Handler mHandler;
+    private static final String CODEC_UPDATE_SERVICE_NAME = "media.codec.update";
+    private static final String[] UPDATE_SERVICE_NAME_ARRAY = {
+            EXTRACTOR_UPDATE_SERVICE_NAME, CODEC_UPDATE_SERVICE_NAME,
+    };
+    private final HashMap<String, IMediaUpdateService> mUpdateServiceMap = new HashMap<>();
+    private final Handler mHandler = new Handler();
 
     public MediaUpdateService(Context context) {
         super(context);
-        mHandler = new Handler();
     }
 
     @Override
     public void onStart() {
         if (("userdebug".equals(android.os.Build.TYPE) || "eng".equals(android.os.Build.TYPE))
                 && !TextUtils.isEmpty(MEDIA_UPDATE_PACKAGE_NAME)) {
-            connect();
+            for (String serviceName : UPDATE_SERVICE_NAME_ARRAY) {
+                connect(serviceName);
+            }
             registerBroadcastReceiver();
         }
     }
 
-    private void connect() {
-        IBinder binder = ServiceManager.getService(EXTRACTOR_UPDATE_SERVICE_NAME);
+    private void connect(final String serviceName) {
+        IBinder binder = ServiceManager.getService(serviceName);
         if (binder != null) {
             try {
                 binder.linkToDeath(new IBinder.DeathRecipient() {
                     @Override
                     public void binderDied() {
-                        Slog.w(TAG, "mediaextractor died; reconnecting");
-                        mMediaExtractorUpdateService = null;
-                        connect();
+                        Slog.w(TAG, "service " + serviceName + " died; reconnecting");
+                        synchronized (mUpdateServiceMap) {
+                            mUpdateServiceMap.remove(serviceName);
+                        }
+                        connect(serviceName);
                     }
                 }, 0);
             } catch (Exception e) {
@@ -77,15 +84,18 @@
             }
         }
         if (binder != null) {
-            mMediaExtractorUpdateService = IMediaExtractorUpdateService.Stub.asInterface(binder);
+            synchronized (mUpdateServiceMap) {
+                mUpdateServiceMap.put(serviceName,
+                        IMediaUpdateService.Stub.asInterface(binder));
+            }
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    packageStateChanged();
+                    packageStateChanged(serviceName);
                 }
             });
         } else {
-            Slog.w(TAG, EXTRACTOR_UPDATE_SERVICE_NAME + " not found.");
+            Slog.w(TAG, serviceName + " not found.");
         }
     }
 
@@ -106,13 +116,12 @@
                                 // following ACTION_PACKAGE_ADDED case.
                                 return;
                             }
-                            packageStateChanged();
-                            break;
+                            // fall-thru
                         case Intent.ACTION_PACKAGE_CHANGED:
-                            packageStateChanged();
-                            break;
                         case Intent.ACTION_PACKAGE_ADDED:
-                            packageStateChanged();
+                            for (String serviceName : UPDATE_SERVICE_NAME_ARRAY) {
+                                packageStateChanged(serviceName);
+                            }
                             break;
                     }
                 }
@@ -128,7 +137,7 @@
                 null /* broadcast permission */, null /* handler */);
     }
 
-    private void packageStateChanged() {
+    private void packageStateChanged(String serviceName) {
         ApplicationInfo packageInfo = null;
         boolean pluginsAvailable = false;
         try {
@@ -144,17 +153,23 @@
                     + " targetSdk:" + packageInfo.targetSdkVersion);
             pluginsAvailable = false;
         }
-        loadExtractorPlugins(
+        loadPlugins(serviceName,
                 (packageInfo != null && pluginsAvailable) ? packageInfo.sourceDir : "");
     }
 
-    private void loadExtractorPlugins(String apkPath) {
+    private void loadPlugins(String serviceName, String apkPath) {
         try {
-            if (mMediaExtractorUpdateService != null) {
-                mMediaExtractorUpdateService.loadPlugins(apkPath);
+            IMediaUpdateService service = null;
+            synchronized (serviceName) {
+                service = mUpdateServiceMap.get(serviceName);
+            }
+            if (service != null) {
+                service.loadPlugins(apkPath);
+            } else {
+                Slog.w(TAG, "service " + serviceName + " passed away");
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Error in loadPlugins", e);
+            Slog.w(TAG, "Error in loadPlugins for " + serviceName, e);
         }
     }
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 452b699..4f4b6bf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -72,6 +72,7 @@
     static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
     static final int NTWK_BLOCKED_BG_RESTRICT = 5;
     static final int NTWK_ALLOWED_DEFAULT = 6;
+    static final int NTWK_ALLOWED_SYSTEM = 7;
 
     private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
     private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 61d67b7..099671d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,6 +16,8 @@
 
 package com.android.server.net;
 
+import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
+
 import android.net.Network;
 import android.net.NetworkTemplate;
 import android.telephony.SubscriptionPlan;
@@ -46,6 +48,28 @@
     public abstract boolean isUidNetworkingBlocked(int uid, String ifname);
 
     /**
+     * Figure out if networking is blocked for a given set of conditions.
+     *
+     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+     * take any locks.
+     *
+     * @param uid The target uid.
+     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+     * @param isNetworkMetered True if the network is metered.
+     * @param isBackgroundRestricted True if data saver is enabled.
+     *
+     * @return true if networking is blocked for the UID under the specified conditions.
+     */
+    public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted) {
+        // Log of invoking internal function is disabled because it will be called very
+        // frequently. And metrics are unlikely needed on this method because the callers are
+        // external and this method doesn't take any locks or perform expensive operations.
+        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, null);
+    }
+
+    /**
      * Informs that an appId has been added or removed from the temp-powersave-whitelist so that
      * that network rules for that appId can be updated.
      *
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 48e09d7..d799642 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -99,6 +99,7 @@
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
 import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
@@ -4837,46 +4838,75 @@
         final long startTime = mStatLogger.getTime();
 
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
-
-        mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
-
-        return ret;
-    }
-
-    private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) {
         final int uidRules;
         final boolean isBackgroundRestricted;
         synchronized (mUidRulesFirstLock) {
             uidRules = mUidRules.get(uid, RULE_NONE);
             isBackgroundRestricted = mRestrictBackground;
         }
-        if (hasRule(uidRules, RULE_REJECT_ALL)) {
-            mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
-            return true;
+        final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, mLogger);
+
+        mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+        return ret;
+    }
+
+    private static boolean isSystem(int uid) {
+        return uid < Process.FIRST_APPLICATION_UID;
+    }
+
+    static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
+            boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
+        final int reason;
+        // Networks are never blocked for system components
+        if (isSystem(uid)) {
+            reason = NTWK_ALLOWED_SYSTEM;
         }
-        if (!isNetworkMetered) {
-            mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
-            return false;
+        else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+            reason = NTWK_BLOCKED_POWER;
         }
-        if (hasRule(uidRules, RULE_REJECT_METERED)) {
-            mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
-            return true;
+        else if (!isNetworkMetered) {
+            reason = NTWK_ALLOWED_NON_METERED;
         }
-        if (hasRule(uidRules, RULE_ALLOW_METERED)) {
-            mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
-            return false;
+        else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+            reason = NTWK_BLOCKED_BLACKLIST;
         }
-        if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
-            mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
-            return false;
+        else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+            reason = NTWK_ALLOWED_WHITELIST;
         }
-        if (isBackgroundRestricted) {
-            mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
-            return true;
+        else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+            reason = NTWK_ALLOWED_TMP_WHITELIST;
         }
-        mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
-        return false;
+        else if (isBackgroundRestricted) {
+            reason = NTWK_BLOCKED_BG_RESTRICT;
+        }
+        else {
+            reason = NTWK_ALLOWED_DEFAULT;
+        }
+
+        final boolean blocked;
+        switch(reason) {
+            case NTWK_ALLOWED_DEFAULT:
+            case NTWK_ALLOWED_NON_METERED:
+            case NTWK_ALLOWED_TMP_WHITELIST:
+            case NTWK_ALLOWED_WHITELIST:
+            case NTWK_ALLOWED_SYSTEM:
+                blocked = false;
+                break;
+            case NTWK_BLOCKED_POWER:
+            case NTWK_BLOCKED_BLACKLIST:
+            case NTWK_BLOCKED_BG_RESTRICT:
+                blocked = true;
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+        if (logger != null) {
+            logger.networkBlocked(uid, reason);
+        }
+
+        return blocked;
     }
 
     private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
@@ -4918,11 +4948,18 @@
         public boolean isUidNetworkingBlocked(int uid, String ifname) {
             final long startTime = mStatLogger.getTime();
 
+            final int uidRules;
+            final boolean isBackgroundRestricted;
+            synchronized (mUidRulesFirstLock) {
+                uidRules = mUidRules.get(uid, RULE_NONE);
+                isBackgroundRestricted = mRestrictBackground;
+            }
             final boolean isNetworkMetered;
             synchronized (mNetworkPoliciesSecondLock) {
                 isNetworkMetered = mMeteredIfaces.contains(ifname);
             }
-            final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+            final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                    isBackgroundRestricted, mLogger);
 
             mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 506cc44..93b83ae 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -719,8 +719,7 @@
                     return;
                 }
                 final long now = System.currentTimeMillis();
-                MetricsLogger.action(r.getLogMaker(now)
-                        .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                MetricsLogger.action(r.getItemLogMaker()
                         .setType(MetricsEvent.TYPE_ACTION)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
@@ -865,8 +864,7 @@
                     r.stats.onExpansionChanged(userAction, expanded);
                     final long now = System.currentTimeMillis();
                     if (userAction) {
-                        MetricsLogger.action(r.getLogMaker(now)
-                                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                        MetricsLogger.action(r.getItemLogMaker()
                                 .setType(expanded ? MetricsEvent.TYPE_DETAIL
                                         : MetricsEvent.TYPE_COLLAPSE));
                     }
@@ -3624,7 +3622,7 @@
                 INotificationListener token, String pkg, UserHandle user,
                 NotificationChannelGroup group) throws RemoteException {
             Preconditions.checkNotNull(user);
-            verifyPrivilegedListener(token, user);
+            verifyPrivilegedListener(token, user, false);
             createNotificationChannelGroup(
                     pkg, getUidForPackageAndUser(pkg, user), group, false, true);
             savePolicyFile();
@@ -3637,7 +3635,7 @@
             Preconditions.checkNotNull(pkg);
             Preconditions.checkNotNull(user);
 
-            verifyPrivilegedListener(token, user);
+            verifyPrivilegedListener(token, user, false);
             updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
         }
 
@@ -3646,7 +3644,7 @@
                 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
             Preconditions.checkNotNull(pkg);
             Preconditions.checkNotNull(user);
-            verifyPrivilegedListener(token, user);
+            verifyPrivilegedListener(token, user, true);
 
             return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
                     false /* includeDeleted */);
@@ -3658,7 +3656,7 @@
                 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
             Preconditions.checkNotNull(pkg);
             Preconditions.checkNotNull(user);
-            verifyPrivilegedListener(token, user);
+            verifyPrivilegedListener(token, user, true);
 
             List<NotificationChannelGroup> groups = new ArrayList<>();
             groups.addAll(mPreferencesHelper.getNotificationChannelGroups(
@@ -3666,13 +3664,18 @@
             return new ParceledListSlice<>(groups);
         }
 
-        private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
+        private void verifyPrivilegedListener(INotificationListener token, UserHandle user,
+                boolean assistantAllowed) {
             ManagedServiceInfo info;
             synchronized (mNotificationLock) {
                 info = mListeners.checkServiceTokenLocked(token);
             }
             if (!hasCompanionDevice(info)) {
-                throw new SecurityException(info + " does not have access");
+                synchronized (mNotificationLock) {
+                    if (!assistantAllowed || !mAssistants.isServiceTokenValidLocked(info.service)) {
+                        throw new SecurityException(info + " does not have access");
+                    }
+                }
             }
             if (!info.enabledAndUserMatches(user.getIdentifier())) {
                 throw new SecurityException(info + " does not have access");
@@ -5842,8 +5845,7 @@
         mArchive.record(r.sbn);
 
         final long now = System.currentTimeMillis();
-        final LogMaker logMaker = r.getLogMaker(now)
-                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+        final LogMaker logMaker = r.getItemLogMaker()
                 .setType(MetricsEvent.TYPE_DISMISS)
                 .setSubtype(reason);
         if (rank != -1 && count != -1) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 65ec580..e9f2718 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -611,6 +611,7 @@
     }
 
     public void applyAdjustments() {
+        long now = System.currentTimeMillis();
         synchronized (mAdjustments) {
             for (Adjustment adjustment: mAdjustments) {
                 Bundle signals = adjustment.getSignals();
@@ -618,17 +619,25 @@
                     final ArrayList<String> people =
                             adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
                     setPeopleOverride(people);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_PEOPLE, people.size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
                     final ArrayList<SnoozeCriterion> snoozeCriterionList =
                             adjustment.getSignals().getParcelableArrayList(
                                     Adjustment.KEY_SNOOZE_CRITERIA);
                     setSnoozeCriteria(snoozeCriterionList);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SNOOZE_CRITERIA,
+                                    snoozeCriterionList.size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
                     final String groupOverrideKey =
                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
                     setOverrideGroupKey(groupOverrideKey);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_GROUP_KEY,
+                                    groupOverrideKey));
                 }
                 if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
                     // Only allow user sentiment update from assistant if user hasn't already
@@ -637,19 +646,31 @@
                             && (getChannel().getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0) {
                         setUserSentiment(adjustment.getSignals().getInt(
                                 Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+                        MetricsLogger.action(getAdjustmentLogMaker()
+                                .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_USER_SENTIMENT,
+                                        getUserSentiment()));
                     }
                 }
                 if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
                     setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_ACTIONS,
+                                    getSmartActions().size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
                     setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_SMART_REPLIES,
+                                    getSmartReplies().size()));
                 }
                 if (signals.containsKey(Adjustment.KEY_IMPORTANCE)) {
                     int importance = signals.getInt(Adjustment.KEY_IMPORTANCE);
                     importance = Math.max(IMPORTANCE_UNSPECIFIED, importance);
                     importance = Math.min(IMPORTANCE_HIGH, importance);
                     setAssistantImportance(importance);
+                    MetricsLogger.action(getAdjustmentLogMaker()
+                            .addTaggedData(MetricsEvent.ADJUSTMENT_KEY_IMPORTANCE,
+                                    importance));
                 }
             }
         }
@@ -732,10 +753,10 @@
     protected void calculateImportance() {
         mImportance = calculateInitialImportance();
         mImportanceExplanation = "app";
-        if (getChannel().isImportanceLocked()) {
+        if (getChannel().hasUserSetImportance()) {
             mImportanceExplanation = "user";
         }
-        if (!getChannel().isImportanceLocked() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+        if (!getChannel().hasUserSetImportance() && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
             mImportance = mAssistantImportance;
             mImportanceExplanation = "asst";
         }
@@ -1203,6 +1224,16 @@
         return getLogMaker(System.currentTimeMillis());
     }
 
+    public LogMaker getItemLogMaker() {
+        return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM);
+    }
+
+    public LogMaker getAdjustmentLogMaker() {
+        return getLogMaker()
+                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT);
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 760f155..44b80c1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -1212,27 +1212,89 @@
     }
 
     private final class Metrics extends Callback {
-        private static final String COUNTER_PREFIX = "dnd_mode_";
+        private static final String COUNTER_MODE_PREFIX = "dnd_mode_";
+        private static final String COUNTER_TYPE_PREFIX = "dnd_type_";
+        private static final int DND_OFF = 0;
+        private static final int DND_ON_MANUAL = 1;
+        private static final int DND_ON_AUTOMATIC = 2;
+        private static final String COUNTER_RULE = "dnd_rule_count";
         private static final long MINIMUM_LOG_PERIOD_MS = 60 * 1000;
 
+        // Total silence, alarms only, priority only
         private int mPreviousZenMode = -1;
-        private long mBeginningMs = 0L;
+        private long mModeLogTimeMs = 0L;
+
+        private int mNumZenRules = -1;
+        private long mRuleCountLogTime = 0L;
+
+        // automatic (1) vs manual (0) vs dnd off (2)
+        private int mPreviousZenType = -1;
+        private long mTypeLogTimeMs = 0L;
 
         @Override
         void onZenModeChanged() {
             emit();
         }
 
+        @Override
+        void onConfigChanged() {
+            emit();
+        }
+
         private void emit() {
             mHandler.postMetricsTimer();
+            emitZenMode();
+            emitRules();
+            emitDndType();
+        }
+
+        private void emitZenMode() {
             final long now = SystemClock.elapsedRealtime();
-            final long since = (now - mBeginningMs);
+            final long since = (now - mModeLogTimeMs);
             if (mPreviousZenMode != mZenMode || since > MINIMUM_LOG_PERIOD_MS) {
                 if (mPreviousZenMode != -1) {
-                    MetricsLogger.count(mContext, COUNTER_PREFIX + mPreviousZenMode, (int) since);
+                    MetricsLogger.count(
+                            mContext, COUNTER_MODE_PREFIX + mPreviousZenMode, (int) since);
                 }
                 mPreviousZenMode = mZenMode;
-                mBeginningMs = now;
+                mModeLogTimeMs = now;
+            }
+        }
+
+        private void emitRules() {
+            final long now = SystemClock.elapsedRealtime();
+            final long since = (now - mRuleCountLogTime);
+            synchronized (mConfig) {
+                int numZenRules = mConfig.automaticRules.size();
+                if (mNumZenRules != numZenRules
+                        || since > MINIMUM_LOG_PERIOD_MS) {
+                    if (mNumZenRules != -1) {
+                        MetricsLogger.count(mContext, COUNTER_RULE,
+                                numZenRules - mNumZenRules);
+                    }
+                    mNumZenRules = numZenRules;
+
+                    mRuleCountLogTime = since;
+                }
+            }
+        }
+
+        private void emitDndType() {
+            final long now = SystemClock.elapsedRealtime();
+            final long since = (now - mTypeLogTimeMs);
+            synchronized (mConfig) {
+                boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
+                int zenType = !dndOn ? DND_OFF
+                        : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
+                if (zenType != mPreviousZenType
+                        || since > MINIMUM_LOG_PERIOD_MS) {
+                    if (mPreviousZenType != -1) {
+                        MetricsLogger.count(
+                                mContext, COUNTER_TYPE_PREFIX + mPreviousZenType, (int) since);
+                    }
+                    mTypeLogTimeMs = now;
+                    mPreviousZenType = zenType;
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index a08c189..404f152 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -659,15 +659,6 @@
             try {
                 final PackageManagerInternal pmInt =
                         LocalServices.getService(PackageManagerInternal.class);
-                ActivityInfo info = pmInt.getActivityInfo(component,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        callingUid, user.getIdentifier());
-                if (!info.exported) {
-                    throw new SecurityException("Cannot launch non-exported components "
-                            + component);
-                }
-
                 // Check that the component actually has Intent.CATEGORY_LAUCNCHER
                 // as calling startActivityAsUser ignores the category and just
                 // resolves based on the component if present.
@@ -680,6 +671,11 @@
                     ActivityInfo activityInfo = apps.get(i).activityInfo;
                     if (activityInfo.packageName.equals(component.getPackageName()) &&
                             activityInfo.name.equals(component.getClassName())) {
+                        if (!activityInfo.exported) {
+                            throw new SecurityException("Cannot launch non-exported components "
+                                    + component);
+                        }
+
                         // Found an activity with category launcher that matches
                         // this component so ok to launch.
                         launchIntent.setPackage(null);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3528b86..9399ebf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -188,6 +188,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
@@ -9538,7 +9539,7 @@
                         }
                     }
                 }
-                if (deleteSandboxData) {
+                if (deleteSandboxData && getStorageManagerInternal() != null) {
                     getStorageManagerInternal().destroySandboxForApp(pkg.packageName, realUserId);
                 }
             } catch (PackageManagerException e) {
@@ -12716,8 +12717,8 @@
 
     @Override
     public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
-            PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage,
-            String callingPackage, int userId) {
+            PersistableBundle appExtras, PersistableBundle launcherExtras,
+            SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
                 "setPackagesSuspendedAsUser");
 
@@ -12762,7 +12763,7 @@
                         unactionedPackages.add(packageName);
                         continue;
                     }
-                    pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
+                    pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
                             launcherExtras, userId);
                     changedPackagesList.add(packageName);
                     changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
@@ -17816,7 +17817,7 @@
                     false /*hidden*/,
                     false /*suspended*/,
                     null /*suspendingPackage*/,
-                    null /*dialogMessage*/,
+                    null /*dialogInfo*/,
                     null /*suspendedAppExtras*/,
                     null /*suspendedLauncherExtras*/,
                     false /*instantApp*/,
@@ -22585,10 +22586,10 @@
         }
 
         @Override
-        public String getSuspendedDialogMessage(String suspendedPackage, int userId) {
+        public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) {
             synchronized (mPackages) {
                 final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
-                return (ps != null) ? ps.readUserState(userId).dialogMessage : null;
+                return (ps != null) ? ps.readUserState(userId).dialogInfo : null;
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 93729d1..e25cca4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -51,6 +51,7 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.ArtManager;
@@ -1701,9 +1702,18 @@
         }
         final String callingPackage =
                 (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
+
+        final SuspendDialogInfo info;
+        if (!TextUtils.isEmpty(dialogMessage)) {
+            info = new SuspendDialogInfo.Builder()
+                    .setMessage(dialogMessage)
+                    .build();
+        } else {
+            info = null;
+        }
         try {
             mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
-                    appExtras, launcherExtras, dialogMessage, callingPackage, userId);
+                    appExtras, launcherExtras, info, callingPackage, userId);
             pw.println("Package " + packageName + " new suspended state: "
                     + mInterface.isPackageSuspendedForUser(packageName, userId));
             return 0;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index fd6aceb..3c22f07 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.Signature;
+import android.content.pm.SuspendDialogInfo;
 import android.os.PersistableBundle;
 import android.service.pm.PackageProto;
 import android.util.ArraySet;
@@ -395,12 +396,12 @@
         return readUserState(userId).suspended;
     }
 
-    void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
+    void setSuspended(boolean suspended, String suspendingPackage, SuspendDialogInfo dialogInfo,
             PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
         final PackageUserState existingUserState = modifyUserState(userId);
         existingUserState.suspended = suspended;
         existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
-        existingUserState.dialogMessage = suspended ? dialogMessage : null;
+        existingUserState.dialogInfo = suspended ? dialogInfo : null;
         existingUserState.suspendedAppExtras = suspended ? appExtras : null;
         existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
     }
@@ -423,7 +424,7 @@
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
-            String dialogMessage, PersistableBundle suspendedAppExtras,
+            SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
             PersistableBundle suspendedLauncherExtras, boolean instantApp,
             boolean virtualPreload, String lastDisableAppCaller,
             ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
@@ -438,7 +439,7 @@
         state.hidden = hidden;
         state.suspended = suspended;
         state.suspendingPackage = suspendingPackage;
-        state.dialogMessage = dialogMessage;
+        state.dialogInfo = dialogInfo;
         state.suspendedAppExtras = suspendedAppExtras;
         state.suspendedLauncherExtras = suspendedLauncherExtras;
         state.lastDisableAppCaller = lastDisableAppCaller;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5c88e06..e2818b7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -49,6 +49,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.net.Uri;
@@ -203,6 +204,7 @@
     private static final String TAG_DEFAULT_BROWSER = "default-browser";
     private static final String TAG_DEFAULT_DIALER = "default-dialer";
     private static final String TAG_VERSION = "version";
+    private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info";
     private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
     private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
 
@@ -222,6 +224,10 @@
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_SUSPENDED = "suspended";
     private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
+    /**
+     * @deprecated Legacy attribute, kept only for upgrading from P builds.
+     */
+    @Deprecated
     private static final String ATTR_SUSPEND_DIALOG_MESSAGE = "suspend_dialog_message";
     // Legacy, uninstall blocks are stored separately.
     @Deprecated
@@ -730,7 +736,7 @@
                                 false /*hidden*/,
                                 false /*suspended*/,
                                 null /*suspendingPackage*/,
-                                null /*dialogMessage*/,
+                                null /*dialogInfo*/,
                                 null /*suspendedAppExtras*/,
                                 null /*suspendedLauncherExtras*/,
                                 instantApp,
@@ -1620,7 +1626,7 @@
                                 false /*hidden*/,
                                 false /*suspended*/,
                                 null /*suspendingPackage*/,
-                                null /*dialogMessage*/,
+                                null /*dialogInfo*/,
                                 null /*suspendedAppExtras*/,
                                 null /*suspendedLauncherExtras*/,
                                 false /*instantApp*/,
@@ -1730,6 +1736,7 @@
                     ArraySet<String> disabledComponents = null;
                     PersistableBundle suspendedAppExtras = null;
                     PersistableBundle suspendedLauncherExtras = null;
+                    SuspendDialogInfo suspendDialogInfo = null;
 
                     int packageDepth = parser.getDepth();
                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1752,20 +1759,28 @@
                             case TAG_SUSPENDED_LAUNCHER_EXTRAS:
                                 suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
                                 break;
+                            case TAG_SUSPENDED_DIALOG_INFO:
+                                suspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
+                                break;
                             default:
                                 Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
                                         + TAG_PACKAGE);
                         }
                     }
+                    if (suspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) {
+                        suspendDialogInfo = new SuspendDialogInfo.Builder()
+                                .setMessage(dialogMessage)
+                                .build();
+                    }
 
                     if (blockUninstall) {
                         setBlockUninstallLPw(userId, name, true);
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
-                            hidden, suspended, suspendingPackage, dialogMessage, suspendedAppExtras,
-                            suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
-                            enabledComponents, disabledComponents, verifState, linkGeneration,
-                            installReason, harmfulAppWarning);
+                            hidden, suspended, suspendingPackage, suspendDialogInfo,
+                            suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
+                            enabledCaller, enabledComponents, disabledComponents, verifState,
+                            linkGeneration, installReason, harmfulAppWarning);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
                 } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -2076,9 +2091,10 @@
                         serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
                                 ustate.suspendingPackage);
                     }
-                    if (ustate.dialogMessage != null) {
-                        serializer.attribute(null, ATTR_SUSPEND_DIALOG_MESSAGE,
-                                ustate.dialogMessage);
+                    if (ustate.dialogInfo != null) {
+                        serializer.startTag(null, TAG_SUSPENDED_DIALOG_INFO);
+                        ustate.dialogInfo.saveToXml(serializer);
+                        serializer.endTag(null, TAG_SUSPENDED_DIALOG_INFO);
                     }
                     if (ustate.suspendedAppExtras != null) {
                         serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
@@ -2618,6 +2634,10 @@
             writeKernelMappingLPr(ps);
         }
 
+        for (final SharedUserSetting sus : mSharedUsers.values()) {
+            knownSet.remove(sus.getSandboxName());
+        }
+
         // Remove any unclaimed mappings
         for (int i = 0; i < knownSet.size(); i++) {
             final String name = knownSet.valueAt(i);
@@ -2628,30 +2648,42 @@
         }
     }
 
+    void writeKernelMappingLPr(SharedUserSetting sus) {
+        if (mKernelMappingFilename == null || sus == null || sus.name == null) return;
+
+        writeKernelMappingLPr(sus.getSandboxName(), sus.userId, sus.getNotInstalledUserIds());
+    }
+
     void writeKernelMappingLPr(PackageSetting ps) {
         if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
 
-        KernelPackageState cur = mKernelMapping.get(ps.name);
+        writeKernelMappingLPr(ps.name, ps.appId, ps.getNotInstalledUserIds());
+        if (ps.sharedUser != null) {
+            writeKernelMappingLPr(ps.sharedUser);
+        }
+    }
+
+    void writeKernelMappingLPr(String name, int appId, int[] excludedUserIds) {
+        KernelPackageState cur = mKernelMapping.get(name);
         final boolean firstTime = cur == null;
-        int[] excludedUserIds = ps.getNotInstalledUserIds();
         final boolean userIdsChanged = firstTime
                 || !Arrays.equals(excludedUserIds, cur.excludedUserIds);
 
         // Package directory
-        final File dir = new File(mKernelMappingFilename, ps.name);
+        final File dir = new File(mKernelMappingFilename, name);
 
         if (firstTime) {
             dir.mkdir();
             // Create a new mapping state
             cur = new KernelPackageState();
-            mKernelMapping.put(ps.name, cur);
+            mKernelMapping.put(name, cur);
         }
 
         // If mapping is incorrect or non-existent, write the appid file
-        if (cur.appId != ps.appId) {
+        if (cur.appId != appId) {
             final File appIdFile = new File(dir, "appid");
-            writeIntToFile(appIdFile, ps.appId);
-            if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
+            writeIntToFile(appIdFile, appId);
+            if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + name + " to " + appId);
         }
 
         if (userIdsChanged) {
@@ -2661,7 +2693,7 @@
                         excludedUserIds[i])) {
                     writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]);
                     if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to "
-                            + ps.name + "/excluded_userids");
+                            + name + "/excluded_userids");
                 }
             }
             // Build the inclusion list -- the ids to remove from the exclusion list
@@ -2671,7 +2703,7 @@
                         writeIntToFile(new File(dir, "clear_userid"),
                                 cur.excludedUserIds[i]);
                         if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to "
-                                + ps.name + "/clear_userid");
+                                + name + "/clear_userid");
 
                     }
                 }
@@ -4737,8 +4769,8 @@
                 final PackageUserState pus = ps.readUserState(user.id);
                 pw.print(" suspendingPackage=");
                 pw.print(pus.suspendingPackage);
-                pw.print(" dialogMessage=");
-                pw.print(pus.dialogMessage);
+                pw.print(" dialogInfo=");
+                pw.print(pus.dialogInfo);
             }
             pw.print(" stopped=");
             pw.print(ps.getStopped(user.id));
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 1a8b2af..32826e5 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -23,6 +23,10 @@
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.util.ArrayUtils;
+
+import libcore.util.EmptyArray;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -144,6 +148,28 @@
         }
     }
 
+    /** Returns userIds which doesn't have any packages with this sharedUserId */
+    public int[] getNotInstalledUserIds() {
+        int[] excludedUserIds = null;
+        for (PackageSetting ps : packages) {
+            final int[] userIds = ps.getNotInstalledUserIds();
+            if (excludedUserIds == null) {
+                excludedUserIds = userIds;
+            } else {
+                for (int userId : excludedUserIds) {
+                    if (!ArrayUtils.contains(userIds, userId)) {
+                        excludedUserIds = ArrayUtils.removeInt(excludedUserIds, userId);
+                    }
+                }
+            }
+        }
+        return excludedUserIds == null ? EmptyArray.INT : excludedUserIds;
+    }
+
+    public String getSandboxName() {
+        return "shared:" + name;
+    }
+
     /** Updates all fields in this shared user setting from another. */
     public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
         copyFrom(sharedUser);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 1315502..dd04652 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -127,7 +127,8 @@
             UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
             UserManager.DISALLOW_AMBIENT_DISPLAY,
             UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT,
-            UserManager.DISALLOW_PRINTING
+            UserManager.DISALLOW_PRINTING,
+            UserManager.DISALLOW_CONFIG_PRIVATE_DNS
     });
 
     /**
@@ -163,7 +164,8 @@
      * User restrictions that cannot be set by profile owners. Applied to all users.
      */
     private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
-            UserManager.DISALLOW_USER_SWITCH
+            UserManager.DISALLOW_USER_SWITCH,
+            UserManager.DISALLOW_CONFIG_PRIVATE_DNS
     );
 
     /**
@@ -741,6 +743,10 @@
                 restriction = UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT;
                 break;
 
+            case android.provider.Settings.Global.PRIVATE_DNS_MODE:
+            case android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER:
+                restriction = UserManager.DISALLOW_CONFIG_PRIVATE_DNS;
+                break;
             default:
                 if (setting.startsWith(Settings.Global.DATA_ROAMING)) {
                     if ("0".equals(value)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 0a93653..45cb477 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -31,9 +31,11 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackagesProvider;
 import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider;
+import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.media.RingtoneManager;
@@ -1182,6 +1184,11 @@
             final int permissionGrantCount = permissionGrants.size();
             for (int j = 0; j < permissionGrantCount; j++) {
                 DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
+                if (!isPermissionDangerous(permissionGrant.name)) {
+                    Log.w(TAG, "Ignoring permission " + permissionGrant.name
+                            + " which isn't dangerous");
+                    continue;
+                }
                 if (permissions == null) {
                     permissions = new ArraySet<>();
                 } else {
@@ -1350,6 +1357,16 @@
                 && pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
     }
 
+    private boolean isPermissionDangerous(String name) {
+        try {
+            final PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(name, 0);
+            return (pi.getProtection() == PermissionInfo.PROTECTION_DANGEROUS);
+        } catch (NameNotFoundException e) {
+            // When unknown assume it's dangerous to be on the safe side
+            return true;
+        }
+    }
+
     private static final class DefaultPermissionGrant {
         final String name;
         final boolean fixed;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 06ee935..c5cee32 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2061,7 +2061,8 @@
                     }
                 });
         mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
-        mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
+        //TODO (b/111365687) : make system context per display.
+        mWindowManagerFuncs.registerPointerEventListener(mSystemGestures, DEFAULT_DISPLAY);
 
         mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
         mLongPressVibePattern = getLongIntArray(mContext.getResources(),
@@ -2258,13 +2259,16 @@
             WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
             lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
             wm.addView(mPointerLocationView, lp);
-            mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView);
+            //TODO (b/111365687) : make system context per display.
+            mWindowManagerFuncs.registerPointerEventListener(mPointerLocationView, DEFAULT_DISPLAY);
         }
     }
 
     private void disablePointerLocation() {
         if (mPointerLocationView != null) {
-            mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView);
+            //TODO (b/111365687) : make system context per display.
+            mWindowManagerFuncs.unregisterPointerEventListener(mPointerLocationView,
+                    DEFAULT_DISPLAY);
             WindowManager wm = (WindowManager) mContext.getSystemService(WINDOW_SERVICE);
             wm.removeView(mPointerLocationView);
             mPointerLocationView = null;
@@ -6860,37 +6864,6 @@
     }
 
     @Override
-    public int getUserRotationMode() {
-        return Settings.System.getIntForUser(mContext.getContentResolver(),
-                Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?
-                        WindowManagerPolicy.USER_ROTATION_FREE :
-                                WindowManagerPolicy.USER_ROTATION_LOCKED;
-    }
-
-    // User rotation: to be used when all else fails in assigning an orientation to the device
-    @Override
-    public void setUserRotationMode(int mode, int rot) {
-        ContentResolver res = mContext.getContentResolver();
-
-        // mUserRotationMode and mUserRotation will be assigned by the content observer
-        if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
-            Settings.System.putIntForUser(res,
-                    Settings.System.USER_ROTATION,
-                    rot,
-                    UserHandle.USER_CURRENT);
-            Settings.System.putIntForUser(res,
-                    Settings.System.ACCELEROMETER_ROTATION,
-                    0,
-                    UserHandle.USER_CURRENT);
-        } else {
-            Settings.System.putIntForUser(res,
-                    Settings.System.ACCELEROMETER_ROTATION,
-                    1,
-                    UserHandle.USER_CURRENT);
-        }
-    }
-
-    @Override
     public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;
         if (safeMode) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 27ab3ef..db13cbc 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -82,7 +82,6 @@
 import android.view.IWindowManager;
 import android.view.InputEventReceiver;
 import android.view.KeyEvent;
-import android.view.Surface;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicyConstants;
@@ -559,10 +558,10 @@
         public Object getWindowManagerLock();
 
         /** Register a system listener for touch events */
-        void registerPointerEventListener(PointerEventListener listener);
+        void registerPointerEventListener(PointerEventListener listener, int displayId);
 
         /** Unregister a system listener for touch events */
-        void unregisterPointerEventListener(PointerEventListener listener);
+        void unregisterPointerEventListener(PointerEventListener listener, int displayId);
 
         /**
          * @return The content insets of the docked divider window.
@@ -1483,26 +1482,6 @@
     public void keepScreenOnStoppedLw();
 
     /**
-     * Gets the current user rotation mode.
-     *
-     * @return The rotation mode.
-     *
-     * @see #USER_ROTATION_LOCKED
-     * @see #USER_ROTATION_FREE
-     */
-    @UserRotationMode
-    public int getUserRotationMode();
-
-    /**
-     * Inform the policy that the user has chosen a preferred orientation ("rotation lock").
-     *
-     * @param mode One of {@link #USER_ROTATION_LOCKED} or {@link #USER_ROTATION_FREE}.
-     * @param rotation One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
-     *                 {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
-     */
-    public void setUserRotationMode(@UserRotationMode int mode, @Surface.Rotation int rotation);
-
-    /**
      * Called when a new system UI visibility is being reported, allowing
      * the policy to adjust what is actually reported.
      * @param visibility The raw visibility reported by the status bar.
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index ed2b79e..4f8e6b6 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -75,6 +75,8 @@
     private static final String KEY_FORCE_BACKGROUND_CHECK = "force_background_check";
     private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
     private static final String KEY_AOD_DISABLED = "aod_disabled";
+    // Go into deep Doze as soon as the screen turns off.
+    private static final String KEY_QUICK_DOZE_ENABLED = "quick_doze_enabled";
     private static final String KEY_SEND_TRON_LOG = "send_tron_log";
 
     private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
@@ -228,6 +230,12 @@
     private boolean mAodDisabled;
 
     /**
+     * Whether Quick Doze is enabled or not.
+     */
+    @GuardedBy("mLock")
+    private boolean mQuickDozeEnabled;
+
+    /**
      * Whether BatterySavingStats should send tron events.
      */
     @GuardedBy("mLock")
@@ -392,6 +400,7 @@
         mForceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK, true);
         mOptionalSensorsDisabled = parser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
         mAodDisabled = parser.getBoolean(KEY_AOD_DISABLED, true);
+        mQuickDozeEnabled = parser.getBoolean(KEY_QUICK_DOZE_ENABLED, false);
         mSendTronLog = parser.getBoolean(KEY_SEND_TRON_LOG, false);
 
         // Get default value from Settings.Secure
@@ -434,6 +443,7 @@
         if (mLaunchBoostDisabled) sb.append("l");
         if (mOptionalSensorsDisabled) sb.append("S");
         if (mAodDisabled) sb.append("o");
+        if (mQuickDozeEnabled) sb.append("q");
         if (mSendTronLog) sb.append("t");
 
         sb.append(mGpsMode);
@@ -502,6 +512,9 @@
                 case ServiceType.AOD:
                     return builder.setBatterySaverEnabled(mAodDisabled)
                             .build();
+                case ServiceType.QUICK_DOZE:
+                    return builder.setBatterySaverEnabled(mQuickDozeEnabled)
+                            .build();
                 default:
                     return builder.setBatterySaverEnabled(realMode)
                             .build();
@@ -562,6 +575,7 @@
             pw.println("  " + KEY_FORCE_BACKGROUND_CHECK + "=" + mForceBackgroundCheck);
             pw.println("  " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
             pw.println("  " + KEY_AOD_DISABLED + "=" + mAodDisabled);
+            pw.println("  " + KEY_QUICK_DOZE_ENABLED + "=" + mQuickDozeEnabled);
             pw.println("  " + KEY_SEND_TRON_LOG + "=" + mSendTronLog);
             pw.println();
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index be24c71..5569822 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -243,7 +243,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService} to update the battery saver stete.
+     * Called by {@link PowerManagerService} to update the battery saver state.
      */
     public void enableBatterySaver(boolean enable, int reason) {
         synchronized (mLock) {
@@ -290,8 +290,8 @@
      * This method is called only in the following cases:
      * - When battery saver becomes activated.
      * - When battery saver becomes deactivated.
-     * - When battery saver is on the interactive state changes.
-     * - When battery saver is on the battery saver policy changes.
+     * - When battery saver is on and the interactive state changes.
+     * - When battery saver is on and the battery saver policy changes.
      */
     void handleBatterySaverStateChanged(boolean sendBroadcast, int reason) {
         final LowPowerModeListener[] listeners;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 6ca4f2e..f10fe58 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -43,12 +43,15 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
 import android.os.IStoraged;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
+import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.Process;
@@ -85,6 +88,7 @@
 import com.android.internal.os.KernelWakelockStats;
 import com.android.internal.os.LooperStats;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.StoragedUidIoStatsReader;
 import com.android.internal.util.DumpUtils;
 import com.android.server.BinderCallsStatsService;
@@ -148,6 +152,13 @@
     public static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
     public static final int DEATH_THRESHOLD = 10;
 
+
+    static final class CompanionHandler extends Handler {
+        CompanionHandler(Looper looper) {
+            super(looper);
+        }
+    }
+
     private final Context mContext;
     private final AlarmManager mAlarmManager;
     @GuardedBy("sStatsdLock")
@@ -164,15 +175,11 @@
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
     private IWifiManager mWifiManager = null;
     private TelephonyManager mTelephony = null;
-    private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
-    private final StatFs mStatFsSystem =
-            new StatFs(Environment.getRootDirectory().getAbsolutePath());
-    private final StatFs mStatFsTemp =
-            new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
     @GuardedBy("sStatsdLock")
     private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
     @GuardedBy("sStatsdLock")
     private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
+    private final CompanionHandler mHandler;
 
     private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
     private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -188,6 +195,8 @@
     private static IThermalService sThermalService;
     private File mBaseDir =
             new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
+    @GuardedBy("this")
+    ProcessCpuTracker mProcessCpuTracker = null;
 
     public StatsCompanionService(Context context) {
         super();
@@ -251,6 +260,11 @@
         } else {
             Slog.e(TAG, "cannot find thermalservice, no throttling push notifications");
         }
+
+        HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        mHandler = new CompanionHandler(handlerThread.getLooper());
+
     }
 
     @Override
@@ -498,7 +512,7 @@
             // only fire when it awakens.
             // AlarmManager will automatically cancel any previous mAnomalyAlarmListener alarm.
             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".anomaly",
-                    mAnomalyAlarmListener, null);
+                    mAnomalyAlarmListener, mHandler);
         } finally {
             Binder.restoreCallingIdentity(callingToken);
         }
@@ -529,7 +543,7 @@
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
             // only fire when it awakens.
             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, timestampMs, TAG + ".periodic",
-                    mPeriodicAlarmListener, null);
+                    mPeriodicAlarmListener, mHandler);
         } finally {
             Binder.restoreCallingIdentity(callingToken);
         }
@@ -561,7 +575,7 @@
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
             // only fire when it awakens.
             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, TAG + ".pull",
-                    mPullingAlarmListener, null);
+                    mPullingAlarmListener, mHandler);
         } finally {
             Binder.restoreCallingIdentity(callingToken);
         }
@@ -756,7 +770,7 @@
     private void pullBluetoothBytesTransfer(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
-        BluetoothActivityEnergyInfo info = pullBluetoothData();
+        BluetoothActivityEnergyInfo info = fetchBluetoothData();
         if (info.getUidTraffic() != null) {
             for (UidTraffic traffic : info.getUidTraffic()) {
                 StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
@@ -868,9 +882,12 @@
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
-        if (mWifiManager == null) {
-            mWifiManager =
-                    IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
+        synchronized (this) {
+            if (mWifiManager == null) {
+                mWifiManager =
+                        IWifiManager.Stub.asInterface(
+                                ServiceManager.getService(Context.WIFI_SERVICE));
+            }
         }
         if (mWifiManager != null) {
             try {
@@ -900,8 +917,10 @@
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
-        if (mTelephony == null) {
-            mTelephony = TelephonyManager.from(mContext);
+        synchronized (this) {
+            if (mTelephony == null) {
+                mTelephony = TelephonyManager.from(mContext);
+            }
         }
         if (mTelephony != null) {
             SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
@@ -925,7 +944,7 @@
     private void pullBluetoothActivityInfo(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
-        BluetoothActivityEnergyInfo info = pullBluetoothData();
+        BluetoothActivityEnergyInfo info = fetchBluetoothData();
         StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
         e.writeLong(info.getTimeStamp());
         e.writeInt(info.getBluetoothStackState());
@@ -936,7 +955,7 @@
         pulledData.add(e);
     }
 
-    private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
+    private synchronized BluetoothActivityEnergyInfo fetchBluetoothData() {
         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null) {
             SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver(
@@ -985,6 +1004,23 @@
         }
     }
 
+    private void pullNativeProcessMemoryState(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        List<ProcessMemoryState> processMemoryStates = LocalServices.getService(
+                ActivityManagerInternal.class).getMemoryStateForNativeProcesses();
+        for (ProcessMemoryState processMemoryState : processMemoryStates) {
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(processMemoryState.uid);
+            e.writeString(processMemoryState.processName);
+            e.writeLong(processMemoryState.pgfault);
+            e.writeLong(processMemoryState.pgmajfault);
+            e.writeLong(processMemoryState.rssInBytes);
+            e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+            pulledData.add(e);
+        }
+    }
+
     private void pullBinderCallsStats(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
@@ -1290,30 +1326,35 @@
 
     private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
-        try {
-            long lastHighWaterMark = readProcStatsHighWaterMark(section);
-            List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
-            long highWaterMark = mProcessStats.getCommittedStats(
-                    lastHighWaterMark, section, true, statsFiles);
-            if (statsFiles.size() != 1) {
-                return;
+        synchronized (this) {
+            try {
+                long lastHighWaterMark = readProcStatsHighWaterMark(section);
+                List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+                long highWaterMark = mProcessStats.getCommittedStats(
+                        lastHighWaterMark, section, true, statsFiles);
+                if (statsFiles.size() != 1) {
+                    return;
+                }
+                InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(
+                        statsFiles.get(0));
+                int[] len = new int[1];
+                byte[] stats = readFully(stream, len);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
+                e.writeStorage(Arrays.copyOf(stats, len[0]));
+                pulledData.add(e);
+                new File(mBaseDir.getAbsolutePath() + "/" + section + "_"
+                        + lastHighWaterMark).delete();
+                new File(
+                        mBaseDir.getAbsolutePath() + "/" + section + "_"
+                                + highWaterMark).createNewFile();
+            } catch (IOException e) {
+                Log.e(TAG, "Getting procstats failed: ", e);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Getting procstats failed: ", e);
+            } catch (SecurityException e) {
+                Log.e(TAG, "Getting procstats failed: ", e);
             }
-            InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
-            int[] len = new int[1];
-            byte[] stats = readFully(stream, len);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-            e.writeStorage(Arrays.copyOf(stats, len[0]));
-            pulledData.add(e);
-            new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark).delete();
-            new File(
-                    mBaseDir.getAbsolutePath() + "/" + section + "_"
-                            + highWaterMark).createNewFile();
-        } catch (IOException e) {
-            Log.e(TAG, "Getting procstats failed: ", e);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Getting procstats failed: ", e);
-        } catch (SecurityException e) {
-            Log.e(TAG, "Getting procstats failed: ", e);
         }
     }
 
@@ -1382,6 +1423,27 @@
         });
     }
 
+    private void pullProcessCpuTime(int tagId, long elapsedNanos, final long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        synchronized (this) {
+            if (mProcessCpuTracker == null) {
+                mProcessCpuTracker = new ProcessCpuTracker(false);
+                mProcessCpuTracker.init();
+            }
+            mProcessCpuTracker.update();
+            for (int i = 0; i < mProcessCpuTracker.countStats(); i++) {
+                ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                        wallClockNanos);
+                e.writeInt(st.uid);
+                e.writeString(st.name);
+                e.writeLong(st.base_utime);
+                e.writeLong(st.base_stime);
+                pulledData.add(e);
+            }
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -1463,6 +1525,10 @@
                 pullProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.NATIVE_PROCESS_MEMORY_STATE: {
+                pullNativeProcessMemoryState(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             case StatsLog.BINDER_CALLS: {
                 pullBinderCallsStats(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
@@ -1512,6 +1578,10 @@
                 pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.PROCESS_CPU_TIME: {
+                pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e5347cf..443d6fe 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.AppProtoEnums;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
@@ -28,19 +29,26 @@
 import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.SparseIntArray;
 
+import android.util.proto.ProtoOutputStream;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.ActivityServiceConnectionsHolder;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.SafeActivityOptions;
 import com.android.server.am.TaskRecord;
+import com.android.server.am.UserState;
 import com.android.server.am.WindowProcessController;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * Activity Task manager local system service interface.
@@ -306,13 +314,13 @@
     public abstract boolean showStrictModeViolationDialog();
     public abstract void showSystemReadyErrorDialogsIfNeeded();
 
-    public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
     public abstract void onProcessMapped(int pid, WindowProcessController proc);
     public abstract void onProcessUnMapped(int pid);
 
     public abstract void onPackageDataCleared(String name);
     public abstract void onPackageUninstalled(String name);
     public abstract void onPackageAdded(String name, boolean replacing);
+    public abstract void onPackageReplaced(ApplicationInfo aInfo);
 
     public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai);
 
@@ -343,4 +351,100 @@
     /** @return true if the given process is the factory test process. */
     public abstract boolean isFactoryTestProcess(WindowProcessController wpc);
     public abstract void updateTopComponentForFactoryTest();
+    public abstract void handleAppDied(WindowProcessController wpc, boolean restarting,
+            Runnable finishInstrumentationCallback);
+    public abstract void closeSystemDialogs(String reason);
+
+    /** Removes all components (e.g. activities, recents, ...) belonging to a disabled package. */
+    public abstract void cleanupDisabledPackageComponents(
+            String packageName, Set<String> disabledClasses, int userId, boolean booted);
+
+    /** Called whenever AM force stops a package. */
+    public abstract boolean onForceStopPackage(String packageName, boolean doit,
+            boolean evenPersistent, int userId);
+    /**
+     * Resumes all top activities in the system if they aren't resumed already.
+     * @param scheduleIdle If the idle message should be schedule after the top activities are
+     *                     resumed.
+     */
+    public abstract void resumeTopActivities(boolean scheduleIdle);
+
+    /** Called by AM just before it binds to an application process. */
+    public abstract void preBindApplication(WindowProcessController wpc);
+
+    /** Called by AM when an application process attaches. */
+    public abstract boolean attachApplication(WindowProcessController wpc) throws RemoteException;
+
+    /** @see IActivityManager#notifyLockedProfile(int) */
+    public abstract void notifyLockedProfile(@UserIdInt int userId, int currentUserId);
+
+    /** @see IActivityManager#startConfirmDeviceCredentialIntent(Intent, Bundle) */
+    public abstract void startConfirmDeviceCredentialIntent(Intent intent, Bundle options);
+
+    /** Writes current activity states to the proto stream. */
+    public abstract void writeActivitiesToProto(ProtoOutputStream proto);
+
+    /**
+     * Saves the current activity manager state and includes the saved state in the next dump of
+     * activity manager.
+     */
+    public abstract void saveANRState(String reason);
+
+    /** Clears the previously saved activity manager ANR state. */
+    public abstract void clearSavedANRState();
+
+    /** Dump the current state based on the command. */
+    public abstract void dump(String cmd, FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage);
+
+    /** Dump the current state for inclusion in process dump. */
+    public abstract boolean dumpForProcesses(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+            String dumpPackage, int dumpAppId, boolean needSep, boolean testPssMode,
+            int wakefulness);
+
+    /** Writes the current window process states to the proto stream. */
+    public abstract void writeProcessesToProto(ProtoOutputStream proto, String dumpPackage);
+
+    /** Dump the current activities state. */
+    public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name,
+            String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly,
+            boolean dumpFocusedStackOnly);
+
+    /** @return true if it the activity management system is okay with GC running now. */
+    public abstract boolean canGcNow();
+
+    /** @return the process for the top-most resumed activity in the system. */
+    public abstract WindowProcessController getTopApp();
+
+    /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
+    public abstract void rankTaskLayersIfNeeded();
+
+    /** Destroy all activities. */
+    public abstract void scheduleDestroyAllActivities(String reason);
+
+    /** Remove user association with activities. */
+    public abstract void removeUser(int userId);
+
+    /** Switch current focused user for activities. */
+    public abstract boolean switchUser(int userId, UserState userState);
+
+    /** Called whenever an app crashes. */
+    public abstract void onHandleAppCrash(WindowProcessController wpc);
+
+    /**
+     * Finish the topmost activities in all stacks that belong to the crashed app.
+     * @param crashedApp The app that crashed.
+     * @param reason Reason to perform this action.
+     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+     */
+    public abstract int finishTopCrashedActivities(
+            WindowProcessController crashedApp, String reason);
+
+    public abstract void onUidActive(int uid, int procState);
+    public abstract void onUidInactive(int uid);
+    public abstract void onActiveUidsCleared();
+    public abstract void onUidProcStateChanged(int uid, int procState);
+
+    public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag);
+    public abstract void onUidRemovedFromPendingTempWhitelist(int uid);
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 642f5781..ca23360 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -145,12 +145,14 @@
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
+import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
+import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
@@ -454,6 +456,8 @@
      */
     WindowState mInputMethodWindow;
 
+    private final PointerEventDispatcher mPointerEventDispatcher;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final AppWindowToken atoken = w.mAppToken;
@@ -833,6 +837,15 @@
         mDisplayReady = true;
 
         mInputMonitor = new InputMonitor(service, mDisplayId);
+
+        if (mService.mInputManager != null) {
+            final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+                    + mDisplayId, mDisplayId);
+            mPointerEventDispatcher = inputChannel != null
+                    ? new PointerEventDispatcher(inputChannel) : null;
+        } else {
+            mPointerEventDispatcher = null;
+        }
     }
 
     boolean isReady() {
@@ -1286,6 +1299,19 @@
 
         mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
                 calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
+
+        // Tap Listeners are supported for:
+        // 1. All physical displays (multi-display).
+        // 2. VirtualDisplays on VR, AA (and everything else).
+        if (mPointerEventDispatcher != null && mTapDetector == null) {
+            if (DEBUG_DISPLAY) {
+                Slog.d(TAG,
+                        "Registering PointerEventListener for DisplayId: " + mDisplayId);
+            }
+            mTapDetector = new TaskTapPointerEventListener(mService, this);
+            registerPointerEventListener(mTapDetector);
+            registerPointerEventListener(mService.mMousePositionTracker);
+        }
     }
 
     /**
@@ -2077,9 +2103,9 @@
     int taskForTapOutside(int x, int y) {
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
-            if (stack.isActivityTypeHome()) {
+            if (stack.isActivityTypeHome() && !stack.inMultiWindowMode()) {
                 // We skip not only home stack, but also everything behind home because user can't
-                // see them.
+                // see them when home stack is isn't in multi-window mode.
                 break;
             }
             final int taskId = stack.taskIdFromPoint(x, y);
@@ -2186,13 +2212,10 @@
         try {
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
-            if (mService.canDispatchPointerEvents()) {
-                if (mTapDetector != null) {
-                    mService.unregisterPointerEventListener(mTapDetector);
-                }
-                if (mDisplayId == DEFAULT_DISPLAY && mService.mMousePositionTracker != null) {
-                    mService.unregisterPointerEventListener(mService.mMousePositionTracker);
-                }
+            if (mPointerEventDispatcher != null && mTapDetector != null) {
+                unregisterPointerEventListener(mTapDetector);
+                unregisterPointerEventListener(mService.mMousePositionTracker);
+                mTapDetector = null;
             }
             mService.mAnimator.removeDisplayLocked(mDisplayId);
             mWindowingLayer.release();
@@ -4409,4 +4432,16 @@
     boolean getLastHasContent() {
         return mLastHasContent;
     }
+
+    void registerPointerEventListener(@NonNull PointerEventListener listener) {
+        if (mPointerEventDispatcher != null) {
+            mPointerEventDispatcher.registerInputEventListener(listener);
+        }
+    }
+
+    void unregisterPointerEventListener(@NonNull PointerEventListener listener) {
+        if (mPointerEventDispatcher != null) {
+            mPointerEventDispatcher.unregisterInputEventListener(listener);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index bd82553..847cff9 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -55,6 +55,7 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayRotation" : TAG_WM;
 
     private final WindowManagerService mService;
+    private final DisplayContent mDisplayContent;
     private final DisplayPolicy mDisplayPolicy;
     private final Context mContext;
     private final Object mLock;
@@ -106,6 +107,7 @@
     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
             DisplayPolicy displayPolicy, Context context, Object lock) {
         mService = service;
+        mDisplayContent = displayContent;
         mDisplayPolicy = displayPolicy;
         mContext = context;
         mLock = lock;
@@ -225,6 +227,70 @@
         }
     }
 
+    void restoreUserRotation(int userRotationMode, int userRotation) {
+        if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
+                && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
+            Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
+                    + " for " + mDisplayContent);
+            userRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+        }
+        if (userRotation < Surface.ROTATION_0 || userRotation > Surface.ROTATION_270) {
+            Slog.w(TAG, "Trying to restore an invalid user rotation " + userRotation
+                    + " for " + mDisplayContent);
+            userRotation = Surface.ROTATION_0;
+        }
+        mUserRotationMode = userRotationMode;
+        mUserRotation = userRotation;
+    }
+
+    private void setUserRotation(int userRotationMode, int userRotation) {
+        if (isDefaultDisplay) {
+            // We'll be notified via settings listener, so we don't need to update internal values.
+            final ContentResolver res = mContext.getContentResolver();
+            final int accelerometerRotation =
+                    userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
+            Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
+                    accelerometerRotation, UserHandle.USER_CURRENT);
+            Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
+                    UserHandle.USER_CURRENT);
+            return;
+        }
+
+        boolean changed = false;
+        if (mUserRotationMode != userRotationMode) {
+            mUserRotationMode = userRotationMode;
+            changed = true;
+        }
+        if (mUserRotation != userRotation) {
+            mUserRotation = userRotation;
+            changed = true;
+        }
+        mService.mDisplaySettings.setUserRotation(mDisplayContent, userRotationMode, userRotation);
+        if (changed) {
+            mService.updateRotation(true /* alwaysSendConfiguration */,
+                    false /* forceRelayout */);
+            mService.mDisplaySettings.writeSettingsLocked();
+        }
+    }
+
+    void freezeRotation(int rotation) {
+        rotation = (rotation == -1) ? mDisplayContent.getRotation() : rotation;
+        setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
+    }
+
+    void thawRotation() {
+        setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
+    }
+
+    boolean isRotationFrozen() {
+        if (!isDefaultDisplay) {
+            return mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED;
+        }
+
+        return Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
+    }
+
     /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
     boolean isDefaultOrientationForced() {
         return mForceDefaultOrientation;
@@ -381,9 +447,6 @@
      * @param orientation An orientation constant, such as
      * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
      * @param lastRotation The most recently used rotation.
-     * @param defaultDisplay Flag indicating whether the rotation is computed for the default
-     *                       display. Currently for all non-default displays sensors, docking mode,
-     *                       rotation lock and other factors are ignored.
      * @return The surface rotation to use.
      */
     int rotationForOrientation(int orientation, int lastRotation) {
@@ -418,8 +481,8 @@
         final int preferredRotation;
         if (!isDefaultDisplay) {
             // For secondary displays we ignore things like displays sensors, docking mode and
-            // rotation lock, and always prefer a default rotation.
-            preferredRotation = Surface.ROTATION_0;
+            // rotation lock, and always prefer user rotation.
+            preferredRotation = mUserRotation;
         } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
             // Ignore sensor when lid switch is open and rotation is forced.
             preferredRotation = mLidOpenRotation;
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index bbb690f..28dc008 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -20,20 +20,23 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.WindowConfiguration;
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Environment;
-import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Surface;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
+import com.android.server.policy.WindowManagerPolicy;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -43,10 +46,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 /**
  * Current persistent settings about a display
  */
@@ -58,15 +57,25 @@
     private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
 
     private static class Entry {
-        private final String name;
-        private int overscanLeft;
-        private int overscanTop;
-        private int overscanRight;
-        private int overscanBottom;
-        private int windowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+        private final String mName;
+        private int mOverscanLeft;
+        private int mOverscanTop;
+        private int mOverscanRight;
+        private int mOverscanBottom;
+        private int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+        private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+        private int mUserRotation = Surface.ROTATION_0;
 
         private Entry(String _name) {
-            name = _name;
+            mName = _name;
+        }
+
+        private boolean isEmpty() {
+            return mOverscanLeft == 0 && mOverscanTop == 0 && mOverscanRight == 0
+                    && mOverscanBottom == 0
+                    && mWindowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
+                    && mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+                    && mUserRotation == Surface.ROTATION_0;
         }
     }
 
@@ -90,13 +99,30 @@
         return entry;
     }
 
+    private Entry getOrCreateEntry(String uniqueId, String name) {
+        Entry entry = getEntry(uniqueId, name);
+        if (entry == null) {
+            entry = new Entry(uniqueId);
+            mEntries.put(uniqueId, entry);
+        }
+        return entry;
+    }
+
+    private void removeEntryIfEmpty(String uniqueId, String name) {
+        final Entry entry = getEntry(uniqueId, name);
+        if (entry.isEmpty()) {
+            mEntries.remove(uniqueId);
+            mEntries.remove(name);
+        }
+    }
+
     private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
         final Entry entry = getEntry(name, uniqueId);
         if (entry != null) {
-            outRect.left = entry.overscanLeft;
-            outRect.top = entry.overscanTop;
-            outRect.right = entry.overscanRight;
-            outRect.bottom = entry.overscanBottom;
+            outRect.left = entry.mOverscanLeft;
+            outRect.top = entry.mOverscanTop;
+            outRect.right = entry.mOverscanRight;
+            outRect.bottom = entry.mOverscanBottom;
         } else {
             outRect.set(0, 0, 0, 0);
         }
@@ -104,28 +130,22 @@
 
     void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
             int bottom) {
-        if (left == 0 && top == 0 && right == 0 && bottom == 0) {
-            // Right now all we are storing is overscan; if there is no overscan,
-            // we have no need for the entry.
-            mEntries.remove(uniqueId);
-            // Legacy name might have been in used, so we need to clear it.
-            mEntries.remove(name);
+        Entry entry = mEntries.get(uniqueId);
+        if (left == 0 && top == 0 && right == 0 && bottom == 0 && entry == null) {
+            // All default value, no action needed.
             return;
         }
-        Entry entry = mEntries.get(uniqueId);
-        if (entry == null) {
-            entry = new Entry(uniqueId);
-            mEntries.put(uniqueId, entry);
-        }
-        entry.overscanLeft = left;
-        entry.overscanTop = top;
-        entry.overscanRight = right;
-        entry.overscanBottom = bottom;
+        entry = getOrCreateEntry(uniqueId, name);
+        entry.mOverscanLeft = left;
+        entry.mOverscanTop = top;
+        entry.mOverscanRight = right;
+        entry.mOverscanBottom = bottom;
+        removeEntryIfEmpty(uniqueId, name);
     }
 
     private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
         final Entry entry = getEntry(name, uniqueId);
-        int windowingMode = entry != null ? entry.windowingMode
+        int windowingMode = entry != null ? entry.mWindowingMode
                 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         // This display used to be in freeform, but we don't support freeform anymore, so fall
         // back to fullscreen.
@@ -148,6 +168,36 @@
         return windowingMode;
     }
 
+    void setUserRotation(DisplayContent dc, int rotationMode, int rotation) {
+        final DisplayInfo displayInfo = dc.getDisplayInfo();
+
+        final String uniqueId = displayInfo.uniqueId;
+        final String name = displayInfo.name;
+        Entry entry = getEntry(displayInfo.name, uniqueId);
+        if (rotationMode == WindowManagerPolicy.USER_ROTATION_FREE
+                && rotation == Surface.ROTATION_0 && entry == null) {
+            // All default values. No action needed.
+            return;
+        }
+
+        entry = getOrCreateEntry(uniqueId, name);
+        entry.mUserRotationMode = rotationMode;
+        entry.mUserRotation = rotation;
+        removeEntryIfEmpty(uniqueId, name);
+    }
+
+    private void restoreUserRotation(DisplayContent dc) {
+        final DisplayInfo info = dc.getDisplayInfo();
+
+        final Entry entry = getEntry(info.name, info.uniqueId);
+        final int userRotationMode = entry != null ? entry.mUserRotationMode
+                : WindowManagerPolicy.USER_ROTATION_FREE;
+        final int userRotation = entry != null ? entry.mUserRotation
+                : Surface.ROTATION_0;
+
+        dc.getDisplayRotation().restoreUserRotation(userRotationMode, userRotation);
+    }
+
     void applySettingsToDisplayLocked(DisplayContent dc) {
         final DisplayInfo displayInfo = dc.getDisplayInfo();
 
@@ -161,6 +211,8 @@
         displayInfo.overscanTop = rect.top;
         displayInfo.overscanRight = rect.right;
         displayInfo.overscanBottom = rect.bottom;
+
+        restoreUserRotation(dc);
     }
 
     void readSettingsLocked() {
@@ -244,12 +296,16 @@
         String name = parser.getAttributeValue(null, "name");
         if (name != null) {
             Entry entry = new Entry(name);
-            entry.overscanLeft = getIntAttribute(parser, "overscanLeft");
-            entry.overscanTop = getIntAttribute(parser, "overscanTop");
-            entry.overscanRight = getIntAttribute(parser, "overscanRight");
-            entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
-            entry.windowingMode = getIntAttribute(parser, "windowingMode",
+            entry.mOverscanLeft = getIntAttribute(parser, "overscanLeft");
+            entry.mOverscanTop = getIntAttribute(parser, "overscanTop");
+            entry.mOverscanRight = getIntAttribute(parser, "overscanRight");
+            entry.mOverscanBottom = getIntAttribute(parser, "overscanBottom");
+            entry.mWindowingMode = getIntAttribute(parser, "windowingMode",
                     WindowConfiguration.WINDOWING_MODE_UNDEFINED);
+            entry.mUserRotationMode = getIntAttribute(parser, "userRotationMode",
+                    WindowManagerPolicy.USER_ROTATION_FREE);
+            entry.mUserRotation = getIntAttribute(parser, "userRotation",
+                    Surface.ROTATION_0);
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
@@ -272,21 +328,28 @@
 
             for (Entry entry : mEntries.values()) {
                 out.startTag(null, "display");
-                out.attribute(null, "name", entry.name);
-                if (entry.overscanLeft != 0) {
-                    out.attribute(null, "overscanLeft", Integer.toString(entry.overscanLeft));
+                out.attribute(null, "name", entry.mName);
+                if (entry.mOverscanLeft != 0) {
+                    out.attribute(null, "overscanLeft", Integer.toString(entry.mOverscanLeft));
                 }
-                if (entry.overscanTop != 0) {
-                    out.attribute(null, "overscanTop", Integer.toString(entry.overscanTop));
+                if (entry.mOverscanTop != 0) {
+                    out.attribute(null, "overscanTop", Integer.toString(entry.mOverscanTop));
                 }
-                if (entry.overscanRight != 0) {
-                    out.attribute(null, "overscanRight", Integer.toString(entry.overscanRight));
+                if (entry.mOverscanRight != 0) {
+                    out.attribute(null, "overscanRight", Integer.toString(entry.mOverscanRight));
                 }
-                if (entry.overscanBottom != 0) {
-                    out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
+                if (entry.mOverscanBottom != 0) {
+                    out.attribute(null, "overscanBottom", Integer.toString(entry.mOverscanBottom));
                 }
-                if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
-                    out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode));
+                if (entry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                    out.attribute(null, "windowingMode", Integer.toString(entry.mWindowingMode));
+                }
+                if (entry.mUserRotationMode != WindowManagerPolicy.USER_ROTATION_FREE) {
+                    out.attribute(null, "userRotationMode",
+                            Integer.toString(entry.mUserRotationMode));
+                }
+                if (entry.mUserRotation != Surface.ROTATION_0) {
+                    out.attribute(null, "userRotation", Integer.toString(entry.mUserRotation));
                 }
                 out.endTag(null, "display");
             }
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index b5e2f01..10d77e5 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -108,7 +108,7 @@
         }
 
         // All the calls below need to happen without the WM lock held since they call into AM.
-        mService.mAmInternal.saveANRState(reason);
+        mService.mAtmInternal.saveANRState(reason);
 
         if (appWindowToken != null && appWindowToken.appToken != null) {
             // Notify the activity manager about the timeout and let it decide whether
@@ -125,7 +125,7 @@
         } else if (windowState != null) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            long timeout = mService.mAtmInternal.inputDispatchingTimedOut(
+            long timeout = mService.mAmInternal.inputDispatchingTimedOut(
                     windowState.mSession.mPid, aboveSystem, reason);
             if (timeout >= 0) {
                 // The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 15f6938..ed3e6c6 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -435,14 +435,14 @@
                 final RecentsAnimationController recentsAnimationController =
                         mService.getRecentsAnimationController();
                 if (recentsAnimationController != null
-                        && recentsAnimationController.hasInputConsumerForApp(w.mAppToken)) {
+                        && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
                             recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
                         addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
-                    // Skip adding the window below regardless of whether there is an input consumer
-                    // to handle it
+                    // If the target app window does not yet exist, then we don't add the input
+                    // consumer window, but also don't add the app window below.
                     return;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6fef1630..5c80759 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -458,10 +458,9 @@
         mRunner = null;
         mCanceled = true;
 
-        // Clear associated input consumers
+        // Update the input windows after the animation is complete
         final InputMonitor inputMonitor =
                 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
-        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
         inputMonitor.updateInputWindowsLw(true /*force*/);
 
         // We have deferred all notifications to the target app as a part of the recents animation,
@@ -494,6 +493,11 @@
     @Override
     public void binderDied() {
         cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
+
+        // Clear associated input consumers on runner death
+        final InputMonitor inputMonitor =
+                mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
     }
 
     void checkAnimationReady(WallpaperController wallpaperController) {
@@ -516,8 +520,14 @@
                 && isTargetOverWallpaper();
     }
 
-    boolean hasInputConsumerForApp(AppWindowToken appToken) {
-        return mInputConsumerEnabled && isAnimatingApp(appToken);
+    /**
+     * @return Whether to use the input consumer to override app input to route home/recents.
+     */
+    boolean shouldApplyInputConsumer(AppWindowToken appToken) {
+        // Only apply the input consumer if it is enabled, it is not the target (home/recents)
+        // being revealed with the transition, and we are actively animating the app as a part of
+        // the animation
+        return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken);
     }
 
     boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
@@ -675,6 +685,7 @@
         final String innerPrefix = prefix + "  ";
         pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
         pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+        pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
         pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
         pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
         pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3fef87d..c8977be 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -228,21 +228,6 @@
             mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
                     displayId, dc.getDisplayInfo());
             dc.configureDisplayPolicy();
-
-            // Tap Listeners are supported for:
-            // 1. All physical displays (multi-display).
-            // 2. VirtualDisplays on VR, AA (and everything else).
-            if (mService.canDispatchPointerEvents()) {
-                if (DEBUG_DISPLAY) {
-                    Slog.d(TAG,
-                            "Registering PointerEventListener for DisplayId: " + displayId);
-                }
-                dc.mTapDetector = new TaskTapPointerEventListener(mService, dc);
-                mService.registerPointerEventListener(dc.mTapDetector);
-                if (displayId == DEFAULT_DISPLAY) {
-                    mService.registerPointerEventListener(mService.mMousePositionTracker);
-                }
-            }
         }
 
         return dc;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b7507a4..ef63b9b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -29,13 +29,13 @@
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.RenderNode;
 import android.os.Environment;
 import android.os.Handler;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
 import android.view.WindowManager.LayoutParams;
@@ -371,7 +371,7 @@
         final RenderNode node = RenderNode.create("TaskSnapshotController", null);
         node.setLeftTopRightBottom(0, 0, width, height);
         node.setClipToBounds(false);
-        final DisplayListCanvas c = node.start(width, height);
+        final RecordingCanvas c = node.start(width, height);
         c.drawColor(color);
         decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
         decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 21e807e..6fd1795 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.graphics.Bitmap.CompressFormat.*;
+
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -361,6 +362,7 @@
 
             // For snapshots with reduced resolution, do not create or save full sized bitmaps
             if (mSnapshot.isReducedResolution()) {
+                swBitmap.recycle();
                 return true;
             }
 
@@ -373,6 +375,8 @@
                 Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
                 return false;
             }
+            reduced.recycle();
+            swBitmap.recycle();
             return true;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 5410676..b096bf2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -430,14 +430,25 @@
     public abstract boolean isUidFocused(int uid);
 
     /**
-     * Checks whether the specified process has IME focus or not.
+     * Checks whether the specified IME client has IME focus or not.
      *
      * @param uid UID of the process to be queried
      * @param pid PID of the process to be queried
-     * @return {@code true} if a process that is identified by {@code uid} and {@code pid} has IME
-     *         focus
+     * @param displayId Display ID reported from the client. Note that this method also verifies
+     *                  whether the specified process is allowed to access to this display or not
+     * @return {@code true} if the IME client specified with {@code uid}, {@code pid}, and
+     *         {@code displayId} has IME focus
      */
-    public abstract boolean isInputMethodClientFocus(int uid, int pid);
+    public abstract boolean isInputMethodClientFocus(int uid, int pid, int displayId);
+
+    /**
+     * Checks whether the given {@code uid} is allowed to use the given {@code displayId} or not.
+     *
+     * @param displayId Display ID to be checked
+     * @param uid UID to be checked.
+     * @return {@code true} if the given {@code uid} is allowed to use the given {@code displayId}
+     */
+    public abstract boolean isUidAllowedOnDisplay(int displayId, int uid);
 
     /**
      * Return the display Id for given window.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 14ea040..5642b1f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -753,8 +753,6 @@
     final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>();
     final BoundsAnimationController mBoundsAnimationController;
 
-    private final PointerEventDispatcher mPointerEventDispatcher;
-
     private WindowContentFrameStats mTempWindowRenderStats;
 
     private final LatencyTracker mLatencyTracker;
@@ -945,14 +943,6 @@
 
         LocalServices.addService(WindowManagerPolicy.class, mPolicy);
 
-        if(mInputManager != null) {
-            final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
-            mPointerEventDispatcher = inputChannel != null
-                    ? new PointerEventDispatcher(inputChannel) : null;
-        } else {
-            mPointerEventDispatcher = null;
-        }
-
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
 
         mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
@@ -2685,8 +2675,9 @@
     public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
         synchronized (mWindowMap) {
             if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.cleanupAnimation(reorderMode);
+                final RecentsAnimationController controller = mRecentsAnimationController;
                 mRecentsAnimationController = null;
+                controller.cleanupAnimation(reorderMode);
                 mAppTransition.updateBooster();
             }
         }
@@ -3126,18 +3117,23 @@
     }
 
     @Override
-    public void registerPointerEventListener(PointerEventListener listener) {
-        mPointerEventDispatcher.registerInputEventListener(listener);
+    public void registerPointerEventListener(PointerEventListener listener, int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null) {
+                displayContent.registerPointerEventListener(listener);
+            }
+        }
     }
 
     @Override
-    public void unregisterPointerEventListener(PointerEventListener listener) {
-        mPointerEventDispatcher.unregisterInputEventListener(listener);
-    }
-
-    /** Check if the service is set to dispatch pointer events. */
-    boolean canDispatchPointerEvents() {
-        return mPointerEventDispatcher != null;
+    public void unregisterPointerEventListener(PointerEventListener listener, int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent != null) {
+                displayContent.unregisterPointerEventListener(listener);
+            }
+        }
     }
 
     // Called by window manager policy. Not exposed externally.
@@ -3672,14 +3668,19 @@
         }
     }
 
+    @Override
+    public void freezeRotation(int rotation) {
+        freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
+    }
+
     /**
      * Freeze rotation changes.  (Enable "rotation lock".)
      * Persists across reboots.
-     * @param rotation The desired rotation to freeze to, or -1 to use the
-     * current rotation.
+     * @param displayId The ID of the display to freeze.
+     * @param rotation The desired rotation to freeze to, or -1 to use the current rotation.
      */
     @Override
-    public void freezeRotation(int rotation) {
+    public void freezeDisplayRotation(int displayId, int rotation) {
         // TODO(multi-display): Track which display is rotated.
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                 "freezeRotation()")) {
@@ -3690,14 +3691,16 @@
                     + "rotation constant.");
         }
 
-        final int defaultDisplayRotation = getDefaultDisplayRotation();
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation="
-                + defaultDisplayRotation);
-
         long origId = Binder.clearCallingIdentity();
         try {
-            mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
-                    rotation == -1 ? defaultDisplayRotation : rotation);
+            synchronized (mWindowMap) {
+                final DisplayContent display = mRoot.getDisplayContent(displayId);
+                if (display == null) {
+                    Slog.w(TAG, "Trying to freeze rotation for a missing display.");
+                    return;
+                }
+                display.getDisplayRotation().freezeRotation(rotation);
+            }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
@@ -3705,12 +3708,17 @@
         updateRotationUnchecked(false, false);
     }
 
+    @Override
+    public void thawRotation() {
+        thawDisplayRotation(Display.DEFAULT_DISPLAY);
+    }
+
     /**
      * Thaw rotation changes.  (Disable "rotation lock".)
      * Persists across reboots.
      */
     @Override
-    public void thawRotation() {
+    public void thawDisplayRotation(int displayId) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                 "thawRotation()")) {
             throw new SecurityException("Requires SET_ORIENTATION permission");
@@ -3721,8 +3729,14 @@
 
         long origId = Binder.clearCallingIdentity();
         try {
-            mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE,
-                    777); // rot not used
+            synchronized (mWindowMap) {
+                final DisplayContent display = mRoot.getDisplayContent(displayId);
+                if (display == null) {
+                    Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+                    return;
+                }
+                display.getDisplayRotation().thawRotation();
+            }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
@@ -3730,6 +3744,23 @@
         updateRotationUnchecked(false, false);
     }
 
+    @Override
+    public boolean isRotationFrozen() {
+        return isDisplayRotationFrozen(Display.DEFAULT_DISPLAY);
+    }
+
+    @Override
+    public boolean isDisplayRotationFrozen(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent display = mRoot.getDisplayContent(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Trying to thaw rotation for a missing display.");
+                return false;
+            }
+            return display.getDisplayRotation().isRotationFrozen();
+        }
+    }
+
     /**
      * Recalculate the current rotation.
      *
@@ -3799,11 +3830,6 @@
     }
 
     @Override
-    public boolean isRotationFrozen() {
-        return mPolicy.getUserRotationMode() == WindowManagerPolicy.USER_ROTATION_LOCKED;
-    }
-
-    @Override
     public int watchRotation(IRotationWatcher watcher, int displayId) {
         final DisplayContent displayContent;
         synchronized (mWindowMap) {
@@ -4816,7 +4842,7 @@
                     synchronized (mWindowMap) {
                         mLastANRState = null;
                     }
-                    mAmInternal.clearSavedANRState();
+                    mAtmInternal.clearSavedANRState();
                 }
                 break;
                 case WALLPAPER_DRAW_PENDING_TIMEOUT: {
@@ -7201,16 +7227,20 @@
         }
 
         @Override
-        public boolean isInputMethodClientFocus(int uid, int pid) {
+        public boolean isInputMethodClientFocus(int uid, int pid, int displayId) {
+            if (displayId == Display.INVALID_DISPLAY) {
+                return false;
+            }
             synchronized (mWindowMap) {
-                // Check all displays if any input method window has focus.
-                for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
-                    final DisplayContent displayContent = mRoot.mChildren.get(i);
-                    if (displayContent.isInputMethodClientFocus(uid, pid)) {
-                        return true;
-                    }
+                final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
+                if (displayContent == null
+                        || displayContent.getDisplayId() != displayId
+                        || !displayContent.hasAccess(uid)) {
+                    return false;
                 }
-
+                if (displayContent.isInputMethodClientFocus(uid, pid)) {
+                    return true;
+                }
                 // Okay, how about this...  what is the current focus?
                 // It seems in some cases we may not have moved the IM
                 // target window, such as when it was in a pop-up window,
@@ -7219,7 +7249,7 @@
                 // press home.  Sometimes the IME won't go down.)
                 // Would be nice to fix this more correctly, but it's
                 // way at the end of a release, and this should be good enough.
-                final WindowState currentFocus = mRoot.getTopFocusedDisplayContent().mCurrentFocus;
+                final WindowState currentFocus = displayContent.mCurrentFocus;
                 if (currentFocus != null && currentFocus.mSession.mUid == uid
                         && currentFocus.mSession.mPid == pid) {
                     return true;
@@ -7229,6 +7259,20 @@
         }
 
         @Override
+        public boolean isUidAllowedOnDisplay(int displayId, int uid) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                return true;
+            }
+            if (displayId == Display.INVALID_DISPLAY) {
+                return false;
+            }
+            synchronized (mWindowMap) {
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                return displayContent != null && displayContent.hasAccess(uid);
+            }
+        }
+
+        @Override
         public int getDisplayIdForWindow(IBinder windowToken) {
             synchronized (mWindowMap) {
                 final WindowState window = mWindowMap.get(windowToken);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 831418b..bf2d0df 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -26,6 +26,7 @@
 import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.Surface;
 
 import java.io.PrintWriter;
 import java.util.regex.Matcher;
@@ -73,6 +74,8 @@
                     // trace files can be written.
                     return mInternal.mWindowTracing.onShellCommand(this,
                             getNextArgRequired());
+                case "set-user-rotation":
+                    return runSetDisplayUserRotation(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -262,6 +265,36 @@
         return Integer.parseInt(s);
     }
 
+    private int runSetDisplayUserRotation(PrintWriter pw) {
+        final String lockMode = getNextArgRequired();
+
+        int displayId = Display.DEFAULT_DISPLAY;
+        String arg = getNextArg();
+        if ("-d".equals(arg)) {
+            displayId = Integer.parseInt(getNextArgRequired());
+            arg = getNextArg();
+        }
+
+        if ("free".equals(lockMode)) {
+            mInternal.thawDisplayRotation(displayId);
+            return 0;
+        }
+
+        if (!lockMode.equals("lock")) {
+            getErrPrintWriter().println("Error: lock mode needs to be either free or lock.");
+            return -1;
+        }
+
+        try {
+            final int rotation = arg != null ? Integer.parseInt(arg) : Surface.ROTATION_0;
+            mInternal.freezeDisplayRotation(displayId, rotation);
+            return 0;
+        } catch (IllegalArgumentException e) {
+            getErrPrintWriter().println("Error: " + e.getMessage());
+            return -1;
+        }
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -279,6 +312,8 @@
         pw.println("    Set display scaling mode.");
         pw.println("  dismiss-keyguard");
         pw.println("    Dismiss the keyguard, prompting user for auth if necessary.");
+        pw.println("  set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
+        pw.println("    Set user rotation mode and user rotation.");
         if (!IS_USER) {
             pw.println("  tracing (start | stop)");
             pw.println("    Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index eacbda1..f16008d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2256,7 +2256,7 @@
     private Configuration getProcessGlobalConfiguration() {
         // For child windows we want to use the pid for the parent window in case the the child
         // window was added from another process.
-        final int pid = isChildWindow() ? getParentWindow().mSession.mPid : mSession.mPid;
+        final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid;
         mTempConfiguration.setTo(mService.mProcessConfigurations.get(
                 pid, mService.mRoot.getConfiguration()));
         return mTempConfiguration;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 045f4eb..15a3a1a3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -99,6 +99,7 @@
         "libutils",
         "libhwui",
         "libbpf",
+        "libnetdbpf",
         "libnetdutils",
         "android.hardware.audio.common@2.0",
         "android.hardware.broadcastradio@1.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c66d03c..3943dba 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -217,7 +217,7 @@
     void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
 
     status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
-            const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
+            const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
     void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
@@ -442,11 +442,11 @@
 }
 
 status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
-        const sp<InputChannel>& inputChannel,
-        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
+        const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle,
+                int32_t displayId) {
     ATRACE_CALL();
-    return mInputManager->getDispatcher()->registerInputChannel(
-            inputChannel, inputWindowHandle, monitor);
+    return mInputManager->getDispatcher()->registerInputChannel(inputChannel, inputWindowHandle,
+            displayId);
 }
 
 status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
@@ -1316,7 +1316,7 @@
 }
 
 static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
+        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jint displayId) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1330,7 +1330,7 @@
             android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
 
     status_t status = im->registerInputChannel(
-            env, inputChannel, inputWindowHandle, monitor);
+            env, inputChannel, inputWindowHandle, displayId);
     if (status) {
         std::string message;
         message += StringPrintf("Failed to register input channel.  status=%d", status);
@@ -1338,7 +1338,8 @@
         return;
     }
 
-    if (! monitor) {
+    // If inputWindowHandle is null and displayId >= 0, treat inputChannel as monitor.
+    if (inputWindowHandle != nullptr || displayId == ADISPLAY_ID_NONE) {
         android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                 handleInputChannelDisposed, im);
     }
@@ -1639,7 +1640,7 @@
     { "nativeHasKeys", "(JII[I[Z)Z",
             (void*) nativeHasKeys },
     { "nativeRegisterInputChannel",
-            "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;Z)V",
+            "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;I)V",
             (void*) nativeRegisterInputChannel },
     { "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
             (void*) nativeUnregisterInputChannel },
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 3302dea..649f1a5 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -30,8 +30,8 @@
 #include <utils/Log.h>
 
 #include "android-base/unique_fd.h"
-#include "bpf/BpfNetworkStats.h"
 #include "bpf/BpfUtils.h"
+#include "netdbpf/BpfNetworkStats.h"
 
 using android::bpf::Stats;
 using android::bpf::hasBpfSupport;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 4350596..2dbbf55 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -15,7 +15,9 @@
  */
 package com.android.server.devicepolicy;
 
+import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
+import android.content.ComponentName;
 
 import com.android.server.SystemService;
 
@@ -68,7 +70,22 @@
     }
 
     @Override
-    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) {
+    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle, int pid,
+            int uid) {
         return false;
     }
+
+    @Override
+    public void setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost) {
+    }
+
+    @Override
+    public int getGlobalPrivateDnsMode(ComponentName who) {
+        return DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
+    }
+
+    @Override
+    public String getGlobalPrivateDnsHost(ComponentName who) {
+        return null;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 913b844..b88165e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,12 +55,18 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 
+import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
+import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
 import static android.provider.Telephony.Carriers.DPC_URI;
 import static android.provider.Telephony.Carriers.ENFORCE_KEY;
 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
@@ -145,6 +151,7 @@
 import android.media.IAudioService;
 import android.net.ConnectivityManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.net.metrics.IpConnectivityLog;
@@ -199,6 +206,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsLog;
 import android.util.Xml;
 import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -395,6 +403,8 @@
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_SLEEP_POLICY);
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
         GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
+        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_MODE);
+        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
 
         GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>();
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON);
@@ -7862,7 +7872,21 @@
     }
 
     @Override
-    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle) {
+    public boolean checkDeviceIdentifierAccess(String packageName, int userHandle, int pid,
+            int uid) {
+        // If the caller is not a system app then it should only be able to check its own device
+        // identifier access.
+        int callingAppId = UserHandle.getAppId(mInjector.binderGetCallingUid());
+        if (callingAppId >= Process.FIRST_APPLICATION_UID
+                && callingAppId != UserHandle.getAppId(uid)) {
+            return false;
+        }
+        // A device or profile owner must also have the READ_PHONE_STATE permission to access device
+        // identifiers. If the package being checked does not have this permission then deny access.
+        if (mContext.checkPermission(android.Manifest.permission.READ_PHONE_STATE, pid, uid)
+                != PackageManager.PERMISSION_GRANTED) {
+            return false;
+        }
         // Allow access to the device owner.
         ComponentName deviceOwner = getDeviceOwnerComponent(true);
         if (deviceOwner != null && deviceOwner.getPackageName().equals(packageName)) {
@@ -9364,6 +9388,7 @@
             }
             saveUserRestrictionsLocked(userHandle);
         }
+        StatsLog.write(StatsLog.USER_RESTRICTION_CHANGED, key, enabledFromThisOwner);
         if (SecurityLog.isLoggingEnabled()) {
             final int eventTag = enabledFromThisOwner
                     ? SecurityLog.TAG_USER_RESTRICTION_ADDED
@@ -13114,4 +13139,78 @@
     private static String getManagedProvisioningPackage(Context context) {
         return context.getResources().getString(R.string.config_managed_provisioning_package);
     }
+
+    private void putPrivateDnsSettings(@Nullable String mode, @Nullable String host) {
+        // Set Private DNS settings using system permissions, as apps cannot write
+        // to global settings.
+        long origId = mInjector.binderClearCallingIdentity();
+        try {
+            mInjector.settingsGlobalPutString(PRIVATE_DNS_MODE, mode);
+            mInjector.settingsGlobalPutString(PRIVATE_DNS_SPECIFIER, host);
+        } finally {
+            mInjector.binderRestoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void setGlobalPrivateDns(@NonNull ComponentName who, int mode, String privateDnsHost) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        enforceDeviceOwner(who);
+
+        switch (mode) {
+            case PRIVATE_DNS_MODE_OPPORTUNISTIC:
+                if (!TextUtils.isEmpty(privateDnsHost)) {
+                    throw new IllegalArgumentException("A DNS host should not be provided when " +
+                            "setting opportunistic mode.");
+                }
+                putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, null);
+                break;
+            case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+                if (!NetworkUtils.isWeaklyValidatedHostname(privateDnsHost)) {
+                    throw new IllegalArgumentException(
+                            String.format("Provided hostname is not valid: %s", privateDnsHost));
+                }
+                putPrivateDnsSettings(ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME,
+                        privateDnsHost);
+                break;
+            default:
+                throw new IllegalArgumentException(String.format("Unsupported mode: %d", mode));
+        }
+    }
+
+    @Override
+    public int getGlobalPrivateDnsMode(@NonNull ComponentName who) {
+        if (!mHasFeature) {
+            return PRIVATE_DNS_MODE_UNKNOWN;
+        }
+
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        enforceDeviceOwner(who);
+        switch (mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE)) {
+            case ConnectivityManager.PRIVATE_DNS_MODE_OFF:
+                return PRIVATE_DNS_MODE_OFF;
+            case ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC:
+                return PRIVATE_DNS_MODE_OPPORTUNISTIC;
+            case ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:
+                return PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+        }
+
+        return PRIVATE_DNS_MODE_UNKNOWN;
+    }
+
+    @Override
+    public String getGlobalPrivateDnsHost(@NonNull ComponentName who) {
+        if (!mHasFeature) {
+            return null;
+        }
+
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        enforceDeviceOwner(who);
+
+        return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
+    }
 }
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 78c0be4..e67f8d3 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -84,7 +84,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
     junit \
-    platform-robolectric-3.6.1-prebuilt
+    platform-robolectric-3.6.2-prebuilt
 
 LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
 LOCAL_MODULE := FrameworksServicesRoboTests
@@ -109,4 +109,4 @@
 
 LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
 
-include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
+include prebuilts/misc/common/robolectric/3.6.2/run_robotests.mk
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 7a847f3..ba1d83e 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -2680,9 +2680,7 @@
         return Files.createTempFile(mContext.getCacheDir().toPath(), "backup", ".tmp");
     }
 
-    private static IterableSubject<
-                    ? extends IterableSubject<?, Path, Iterable<Path>>, Path, Iterable<Path>>
-            assertDirectory(Path directory) throws IOException {
+    private static IterableSubject assertDirectory(Path directory) throws IOException {
         return assertThat(oneTimeIterable(Files.newDirectoryStream(directory).iterator()))
                 .named("directory " + directory);
     }
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
index 8c02833..b21b3e4 100644
--- a/services/tests/mockingservicestests/Android.mk
+++ b/services/tests/mockingservicestests/Android.mk
@@ -20,13 +20,11 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    frameworks-base-testutils \
     services.core \
-    androidx-test \
+    services.net \
+    androidx.test.runner \
     mockito-target-extended-minus-junit4 \
     platform-test-annotations \
-    ShortcutManagerTestUtils \
-    truth-prebuilt \
 
 LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index de3d285..f85ffc8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -30,10 +30,20 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME;
+import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME;
+import static com.android.server.AlarmManagerService.Constants
+        .KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+import static com.android.server.AlarmManagerService.Constants.KEY_LISTENER_TIMEOUT;
+import static com.android.server.AlarmManagerService.Constants.KEY_MAX_INTERVAL;
+import static com.android.server.AlarmManagerService.Constants.KEY_MIN_FUTURITY;
+import static com.android.server.AlarmManagerService.Constants.KEY_MIN_INTERVAL;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -45,6 +55,7 @@
 import android.app.IUidObserver;
 import android.app.PendingIntent;
 import android.app.usage.UsageStatsManagerInternal;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
@@ -52,12 +63,16 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Log;
+import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.annotations.GuardedBy;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -66,8 +81,9 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 
-import javax.annotation.concurrent.GuardedBy;
+import java.util.ArrayList;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -80,6 +96,8 @@
 
     private AlarmManagerService mService;
     @Mock
+    private ContentResolver mMockResolver;
+    @Mock
     private IActivityManager mIActivityManager;
     @Mock
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -186,9 +204,11 @@
     public final void setUp() throws Exception {
         mMockingSession = mockitoSession()
                 .initMocks(this)
-                .mockStatic(ActivityManager.class, Answers.CALLS_REAL_METHODS)
+                .spyStatic(ActivityManager.class)
                 .mockStatic(LocalServices.class)
-                .mockStatic(Looper.class, Answers.CALLS_REAL_METHODS)
+                .spyStatic(Looper.class)
+                .spyStatic(Settings.Global.class)
+                .strictness(Strictness.WARN)
                 .startMocking();
         doReturn(mIActivityManager).when(ActivityManager::getService);
         doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
@@ -201,15 +221,19 @@
                 .thenReturn(STANDBY_BUCKET_ACTIVE);
         doReturn(Looper.getMainLooper()).when(Looper::myLooper);
 
-        final Context context = InstrumentationRegistry.getTargetContext();
-        mInjector = spy(new Injector(context));
+        final Context context = spy(InstrumentationRegistry.getTargetContext());
+        when(context.getContentResolver()).thenReturn(mMockResolver);
+        doNothing().when(mMockResolver).registerContentObserver(any(), anyBoolean(), any());
+        doReturn("min_futurity=0").when(() ->
+                Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
+        mInjector = new Injector(context);
         mService = new AlarmManagerService(context, mInjector);
         spyOn(mService);
         doNothing().when(mService).publishBinderService(any(), any());
         mService.onStart();
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-        mService.mConstants.MIN_FUTURITY = 0;
 
+        assertEquals(0, mService.mConstants.MIN_FUTURITY);
         assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
         assertEquals(mService.mClockReceiver, mClockReceiver);
         assertEquals(mService.mWakeLock, mWakeLock);
@@ -235,7 +259,6 @@
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
-        verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime);
         assertEquals(triggerTime, mTestTimer.getElapsed());
     }
 
@@ -257,12 +280,45 @@
     }
 
     @Test
+    public void testUpdateConstants() {
+        final StringBuilder constantsBuilder = new StringBuilder();
+        constantsBuilder.append(KEY_MIN_FUTURITY);
+        constantsBuilder.append("=5,");
+        constantsBuilder.append(KEY_MIN_INTERVAL);
+        constantsBuilder.append("=10,");
+        constantsBuilder.append(KEY_MAX_INTERVAL);
+        constantsBuilder.append("=15,");
+        constantsBuilder.append(KEY_ALLOW_WHILE_IDLE_SHORT_TIME);
+        constantsBuilder.append("=20,");
+        constantsBuilder.append(KEY_ALLOW_WHILE_IDLE_LONG_TIME);
+        constantsBuilder.append("=25,");
+        constantsBuilder.append(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+        constantsBuilder.append("=30,");
+        constantsBuilder.append(KEY_LISTENER_TIMEOUT);
+        constantsBuilder.append("=35,");
+
+        doReturn(constantsBuilder.toString()).when(() -> Settings.Global.getString(mMockResolver,
+                Settings.Global.ALARM_MANAGER_CONSTANTS));
+        mService.mConstants.onChange(false, null);
+        assertEquals(5, mService.mConstants.MIN_FUTURITY);
+        assertEquals(10, mService.mConstants.MIN_INTERVAL);
+        assertEquals(15, mService.mConstants.MAX_INTERVAL);
+        assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_SHORT_TIME);
+        assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_LONG_TIME);
+        assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+        assertEquals(35, mService.mConstants.LISTENER_TIMEOUT);
+    }
+
+    @Test
     public void testMinFuturity() {
-        mService.mConstants.MIN_FUTURITY = 10;
+        doReturn("min_futurity=10").when(() ->
+                Settings.Global.getString(mMockResolver, Settings.Global.ALARM_MANAGER_CONSTANTS));
+        mService.mConstants.onChange(false, null);
+        assertEquals(10, mService.mConstants.MIN_FUTURITY);
         final long triggerTime = mNowElapsedTest + 1;
         final long expectedTriggerTime = mNowElapsedTest + mService.mConstants.MIN_FUTURITY;
         setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, getNewMockPendingIntent());
-        verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, expectedTriggerTime);
+        assertEquals(expectedTriggerTime, mTestTimer.getElapsed());
     }
 
     @Test
@@ -344,6 +400,31 @@
                 () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
     }
 
+    @Test
+    public void testAlarmRestrictedInBatterSaver() throws PendingIntent.CanceledException {
+        final ArgumentCaptor<AppStateTracker.Listener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(AppStateTracker.Listener.class);
+        verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture());
+
+        final PendingIntent alarmPi = getNewMockPendingIntent();
+        when(mAppStateTracker.areAlarmsRestricted(TEST_CALLING_UID, TEST_CALLING_PACKAGE,
+                false)).thenReturn(true);
+        setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, alarmPi);
+        assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed());
+
+        final SparseArray<ArrayList<AlarmManagerService.Alarm>> restrictedAlarms =
+                mService.mPendingBackgroundAlarms;
+        assertNull(restrictedAlarms.get(TEST_CALLING_UID));
+
+        mNowElapsedTest = mTestTimer.expire();
+        pollingCheck(DEFAULT_TIMEOUT, () -> (restrictedAlarms.get(TEST_CALLING_UID) != null));
+
+        listenerArgumentCaptor.getValue().unblockAlarmsForUid(TEST_CALLING_UID);
+        verify(alarmPi).send(any(Context.class), eq(0), any(Intent.class), any(),
+                any(Handler.class), isNull(), any());
+        assertNull(restrictedAlarms.get(TEST_CALLING_UID));
+    }
+
     @After
     public void tearDown() {
         if (mMockingSession != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 95ed00f..04a8408 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -36,6 +36,7 @@
 import static com.android.server.DeviceIdleController.STATE_IDLE_PENDING;
 import static com.android.server.DeviceIdleController.STATE_INACTIVE;
 import static com.android.server.DeviceIdleController.STATE_LOCATING;
+import static com.android.server.DeviceIdleController.STATE_QUICK_DOZE_DELAY;
 import static com.android.server.DeviceIdleController.STATE_SENSING;
 import static com.android.server.DeviceIdleController.lightStateToString;
 import static com.android.server.DeviceIdleController.stateToString;
@@ -46,6 +47,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 
 import android.app.ActivityManagerInternal;
@@ -53,13 +55,17 @@
 import android.app.IActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.SensorManager;
 import android.location.LocationManager;
 import android.location.LocationProvider;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
 import android.os.SystemClock;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -83,11 +89,14 @@
     private DeviceIdleController mDeviceIdleController;
     private AnyMotionDetectorForTest mAnyMotionDetector;
     private AppStateTrackerForTest mAppStateTracker;
+    private InjectorForTest mInjector;
 
     private MockitoSession mMockingSession;
     @Mock
     private AlarmManager mAlarmManager;
     @Mock
+    private ConnectivityService mConnectivityService;
+    @Mock
     private DeviceIdleController.Constants mConstants;
     @Mock
     private IActivityManager mIActivityManager;
@@ -97,8 +106,12 @@
     private PowerManager mPowerManager;
     @Mock
     private PowerManager.WakeLock mWakeLock;
+    @Mock
+    private PowerManagerInternal mPowerManagerInternal;
 
     class InjectorForTest extends DeviceIdleController.Injector {
+        ConnectivityService connectivityService;
+        LocationManager locationManager;
 
         InjectorForTest(Context ctx) {
             super(ctx);
@@ -122,18 +135,19 @@
 
         @Override
         ConnectivityService getConnectivityService() {
-            return null;
+            return connectivityService;
         }
 
         @Override
-        DeviceIdleController.Constants getConstants(DeviceIdleController controller, Handler handler,
+        DeviceIdleController.Constants getConstants(DeviceIdleController controller,
+                Handler handler,
                 ContentResolver resolver) {
             return mConstants;
         }
 
         @Override
         LocationManager getLocationManager() {
-            return mLocationManager;
+            return locationManager;
         }
 
         @Override
@@ -193,16 +207,19 @@
                 .when(() -> LocalServices.getService(ActivityManagerInternal.class));
         doReturn(mock(ActivityTaskManagerInternal.class))
                 .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class));
-        doReturn(mock(PowerManagerInternal.class))
+        doReturn(mPowerManagerInternal)
                 .when(() -> LocalServices.getService(PowerManagerInternal.class));
+        when(mPowerManagerInternal.getLowPowerState(anyInt())).thenReturn(
+                mock(PowerSaveState.class));
         doReturn(mock(NetworkPolicyManagerInternal.class))
                 .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class));
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
         doNothing().when(mWakeLock).acquire();
+        doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
         mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
         mAnyMotionDetector = new AnyMotionDetectorForTest();
-        mDeviceIdleController = new DeviceIdleController(getContext(),
-                new InjectorForTest(getContext()));
+        mInjector = new InjectorForTest(getContext());
+        mDeviceIdleController = new DeviceIdleController(getContext(), mInjector);
         spyOn(mDeviceIdleController);
         doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
         mDeviceIdleController.onStart();
@@ -271,6 +288,82 @@
     }
 
     @Test
+    public void testUpdateConnectivityState() {
+        // No connectivity service
+        final boolean isConnected = mDeviceIdleController.isNetworkConnected();
+        mInjector.connectivityService = null;
+        mDeviceIdleController.updateConnectivityState(null);
+        assertEquals(isConnected, mDeviceIdleController.isNetworkConnected());
+
+        // No active network info
+        mInjector.connectivityService = mConnectivityService;
+        doReturn(null).when(mConnectivityService).getActiveNetworkInfo();
+        mDeviceIdleController.updateConnectivityState(null);
+        assertFalse(mDeviceIdleController.isNetworkConnected());
+
+        // Active network info says connected.
+        final NetworkInfo ani = mock(NetworkInfo.class);
+        doReturn(ani).when(mConnectivityService).getActiveNetworkInfo();
+        doReturn(true).when(ani).isConnected();
+        mDeviceIdleController.updateConnectivityState(null);
+        assertTrue(mDeviceIdleController.isNetworkConnected());
+
+        // Active network info says not connected.
+        doReturn(false).when(ani).isConnected();
+        mDeviceIdleController.updateConnectivityState(null);
+        assertFalse(mDeviceIdleController.isNetworkConnected());
+
+        // Wrong intent passed (false).
+        Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 3);
+        doReturn(true).when(ani).isConnected();
+        doReturn(1).when(ani).getType();
+        mDeviceIdleController.updateConnectivityState(intent);
+        // Wrong intent means we shouldn't update the connected state.
+        assertFalse(mDeviceIdleController.isNetworkConnected());
+
+        // Intent says connected.
+        doReturn(1).when(ani).getType();
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 1);
+        intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+        mDeviceIdleController.updateConnectivityState(intent);
+        assertTrue(mDeviceIdleController.isNetworkConnected());
+
+        // Wrong intent passed (true).
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 3);
+        // Wrong intent means we shouldn't update the connected state.
+        assertTrue(mDeviceIdleController.isNetworkConnected());
+
+        // Intent says not connected.
+        intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 1);
+        intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
+        mDeviceIdleController.updateConnectivityState(intent);
+        assertFalse(mDeviceIdleController.isNetworkConnected());
+    }
+
+    @Test
+    public void testUpdateQuickDozeFlagLocked() {
+        mDeviceIdleController.updateQuickDozeFlagLocked(false);
+        assertFalse(mDeviceIdleController.isQuickDozeEnabled());
+
+        // Make sure setting false when quick doze is already off doesn't change anything.
+        mDeviceIdleController.updateQuickDozeFlagLocked(false);
+        assertFalse(mDeviceIdleController.isQuickDozeEnabled());
+
+        // Test changing from quick doze off to quick doze on.
+        mDeviceIdleController.updateQuickDozeFlagLocked(true);
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+
+        // Make sure setting true when quick doze is already on doesn't change anything.
+        mDeviceIdleController.updateQuickDozeFlagLocked(true);
+        assertTrue(mDeviceIdleController.isQuickDozeEnabled());
+
+        // Test changing from quick doze on to quick doze off.
+        mDeviceIdleController.updateQuickDozeFlagLocked(false);
+        assertFalse(mDeviceIdleController.isQuickDozeEnabled());
+    }
+
+    @Test
     public void testStateActiveToStateInactive_ConditionsNotMet() {
         mDeviceIdleController.becomeActiveLocked("testing", 0);
         verifyStateConditions(STATE_ACTIVE);
@@ -351,6 +444,46 @@
     }
 
     @Test
+    public void testTransitionFromAnyStateToStateQuickDozeDelay() {
+        enterDeepState(STATE_ACTIVE);
+        setQuickDozeEnabled(true);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_INACTIVE);
+        setQuickDozeEnabled(true);
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_IDLE_PENDING);
+        setQuickDozeEnabled(true);
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_SENSING);
+        setQuickDozeEnabled(true);
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_LOCATING);
+        setQuickDozeEnabled(true);
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        // IDLE should stay as IDLE.
+        enterDeepState(STATE_IDLE);
+        setQuickDozeEnabled(true);
+        verifyStateConditions(STATE_IDLE);
+
+        // IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE.
+        enterDeepState(STATE_IDLE_MAINTENANCE);
+        setQuickDozeEnabled(true);
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+    }
+
+    @Test
     public void testStepIdleStateLocked_InvalidStates() {
         mDeviceIdleController.becomeActiveLocked("testing", 0);
         mDeviceIdleController.stepIdleStateLocked("testing");
@@ -360,10 +493,86 @@
     }
 
     @Test
+    public void testStepIdleStateLocked_ValidStates_QuickDoze() {
+        setAlarmSoon(false);
+
+        // Quick doze should go directly into IDLE.
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+
+        // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+    }
+
+    @Test
+    public void testStepIdleStateLocked_ValidStates_WithWakeFromIdleAlarmSoon() {
+        enterDeepState(STATE_ACTIVE);
+        // Return that there's an alarm coming soon.
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_ACTIVE);
+
+        // Everything besides ACTIVE should end up as INACTIVE since the screen would be off.
+
+        enterDeepState(STATE_INACTIVE);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+
+        enterDeepState(STATE_IDLE_PENDING);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+
+        enterDeepState(STATE_SENSING);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+
+        enterDeepState(STATE_LOCATING);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+
+        // With quick doze enabled, we should end up in QUICK_DOZE_DELAY instead of INACTIVE.
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        setQuickDozeEnabled(true);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        // With quick doze disabled, we should end up in INACTIVE instead of QUICK_DOZE_DELAY.
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        setQuickDozeEnabled(false);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+
+        enterDeepState(STATE_IDLE);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+
+        enterDeepState(STATE_IDLE_MAINTENANCE);
+        setAlarmSoon(true);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_INACTIVE);
+    }
+
+    @Test
     public void testStepIdleStateLocked_ValidStates_NoLocationManager() {
-        mDeviceIdleController.setLocationManagerForTest(null);
+        mInjector.locationManager = null;
         // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
-        doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        setAlarmSoon(false);
         // Set state to INACTIVE.
         mDeviceIdleController.becomeActiveLocked("testing", 0);
         setChargingOn(false);
@@ -395,7 +604,7 @@
     @Test
     public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() {
         // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
-        doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        setAlarmSoon(false);
         // Set state to INACTIVE.
         mDeviceIdleController.becomeActiveLocked("testing", 0);
         setChargingOn(false);
@@ -427,10 +636,10 @@
 
     @Test
     public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
+        mInjector.locationManager = mLocationManager;
         doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
         // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
-        // TODO: add tests for when there's a wake-from-idle alarm coming soon.
-        doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        setAlarmSoon(false);
         // Set state to INACTIVE.
         mDeviceIdleController.becomeActiveLocked("testing", 0);
         setChargingOn(false);
@@ -463,6 +672,162 @@
     }
 
     @Test
+    public void testLightStepIdleStateLocked_InvalidStates() {
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        // stepLightIdleStateLocked doesn't handle the ACTIVE case, so the state
+        // should stay as ACTIVE.
+        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+    }
+
+    /**
+     * Make sure stepLightIdleStateLocked doesn't change state when the state is
+     * LIGHT_STATE_OVERRIDE.
+     */
+    @Test
+    public void testLightStepIdleStateLocked_Overriden() {
+        enterLightState(LIGHT_STATE_OVERRIDE);
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
+    }
+
+    @Test
+    public void testLightStepIdleStateLocked_ValidStates_NoActiveOps_NetworkConnected() {
+        setNetworkConnected(true);
+        mDeviceIdleController.setJobsActive(false);
+        mDeviceIdleController.setAlarmsActive(false);
+        mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+        // No active ops means INACTIVE should go straight to IDLE.
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+    }
+
+    @Test
+    public void testLightStepIdleStateLocked_ValidStates_ActiveOps_NetworkConnected() {
+        setNetworkConnected(true);
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+        // Active ops means INACTIVE should go to PRE_IDLE to wait.
+        mDeviceIdleController.setJobsActive(true);
+        mDeviceIdleController.setAlarmsActive(true);
+        mDeviceIdleController.setActiveIdleOpsForTest(1);
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
+
+        // Even with active ops, PRE_IDLE should go to IDLE.
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+    }
+
+    @Test
+    public void testLightStepIdleStateLocked_ValidStates_NoActiveOps_NoNetworkConnected() {
+        setNetworkConnected(false);
+        mDeviceIdleController.setJobsActive(false);
+        mDeviceIdleController.setAlarmsActive(false);
+        mDeviceIdleController.setActiveIdleOpsForTest(0);
+
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+        // No active ops means INACTIVE should go straight to IDLE.
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        // Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+    }
+
+    @Test
+    public void testLightStepIdleStateLocked_ValidStates_ActiveOps_NoNetworkConnected() {
+        setNetworkConnected(false);
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+
+        // Active ops means INACTIVE should go to PRE_IDLE to wait.
+        mDeviceIdleController.setJobsActive(true);
+        mDeviceIdleController.setAlarmsActive(true);
+        mDeviceIdleController.setActiveIdleOpsForTest(1);
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_PRE_IDLE);
+
+        // Even with active ops, PRE_IDLE should go to IDLE.
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        // Should cycle between IDLE, WAITING_FOR_NETWORK, and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_WAITING_FOR_NETWORK);
+
+        mDeviceIdleController.stepLightIdleStateLocked("testing");
+        verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
+    }
+
+    ///////////////// EXIT conditions ///////////////////
+
+    @Test
     public void testExitMaintenanceEarlyIfNeededLocked_deep_noActiveOps() {
         mDeviceIdleController.setJobsActive(false);
         mDeviceIdleController.setAlarmsActive(false);
@@ -499,6 +864,10 @@
         mDeviceIdleController.setActiveIdleOpsForTest(0);
         mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
         verifyStateConditions(STATE_IDLE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
     }
 
     @Test
@@ -536,6 +905,10 @@
         enterDeepState(STATE_IDLE_MAINTENANCE);
         mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
         verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
     }
 
     @Test
@@ -573,6 +946,10 @@
         enterDeepState(STATE_IDLE_MAINTENANCE);
         mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
         verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
     }
 
     @Test
@@ -610,6 +987,10 @@
         enterDeepState(STATE_IDLE_MAINTENANCE);
         mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
         verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.exitMaintenanceEarlyIfNeededLocked();
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
     }
 
     @Test
@@ -763,36 +1144,97 @@
     }
 
     @Test
-    public void testHandleMotionDetectedLocked_deep() {
+    public void testHandleMotionDetectedLocked_deep_quickDoze_off() {
         enterDeepState(STATE_ACTIVE);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_ACTIVE);
 
         // Anything that wasn't ACTIVE before motion detection should end up in the INACTIVE state.
 
         enterDeepState(STATE_INACTIVE);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_INACTIVE);
 
         enterDeepState(STATE_IDLE_PENDING);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_INACTIVE);
 
         enterDeepState(STATE_SENSING);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_INACTIVE);
 
         enterDeepState(STATE_LOCATING);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_INACTIVE);
 
         enterDeepState(STATE_IDLE);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_INACTIVE);
 
         enterDeepState(STATE_IDLE_MAINTENANCE);
+        setQuickDozeEnabled(false);
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
         verifyStateConditions(STATE_INACTIVE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        setQuickDozeEnabled(false);
+        // Disabling quick doze doesn't immediately change the state as coming out is harder than
+        // going in.
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_INACTIVE);
+    }
+
+    @Test
+    public void testHandleMotionDetectedLocked_deep_quickDoze_on() {
+        enterDeepState(STATE_ACTIVE);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_ACTIVE);
+
+        // Anything that wasn't ACTIVE before motion detection should end up in the
+        // QUICK_DOZE_DELAY state since quick doze is enabled.
+
+        enterDeepState(STATE_INACTIVE);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_IDLE_PENDING);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_SENSING);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_LOCATING);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_IDLE);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_IDLE_MAINTENANCE);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        setQuickDozeEnabled(true);
+        mDeviceIdleController.handleMotionDetectedLocked(50, "test");
+        verifyStateConditions(STATE_QUICK_DOZE_DELAY);
     }
 
     @Test
@@ -861,6 +1303,10 @@
         enterDeepState(STATE_IDLE_MAINTENANCE);
         mDeviceIdleController.becomeActiveLocked("test", 1000);
         verifyStateConditions(STATE_ACTIVE);
+
+        enterDeepState(STATE_QUICK_DOZE_DELAY);
+        mDeviceIdleController.becomeActiveLocked("test", 1000);
+        verifyStateConditions(STATE_ACTIVE);
     }
 
     @Test
@@ -902,7 +1348,16 @@
                 setScreenOn(true);
                 mDeviceIdleController.becomeActiveLocked("testing", 0);
                 break;
+            case STATE_QUICK_DOZE_DELAY:
+                // Start off from ACTIVE in case we're already past the desired state.
+                enterDeepState(STATE_ACTIVE);
+                setQuickDozeEnabled(true);
+                setScreenOn(false);
+                setChargingOn(false);
+                mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+                break;
             case STATE_LOCATING:
+                mInjector.locationManager = mLocationManager;
                 doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(
                         anyString());
                 // Fallthrough to step loop.
@@ -912,12 +1367,14 @@
             case STATE_IDLE_MAINTENANCE:
                 // Make sure the controller doesn't think there's a wake-from-idle alarm coming
                 // soon.
-                doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+                setAlarmSoon(false);
             case STATE_INACTIVE:
+                // Start off from ACTIVE in case we're already past the desired state.
+                enterDeepState(STATE_ACTIVE);
+                setQuickDozeEnabled(false);
                 setScreenOn(false);
                 setChargingOn(false);
                 mDeviceIdleController.becomeInactiveIfAppropriateLocked();
-                //fail(stateToString(mDeviceIdleController.getState()));
                 int count = 0;
                 while (mDeviceIdleController.getState() != state) {
                     // Stepping through each state ensures that the proper features are turned
@@ -925,7 +1382,8 @@
                     mDeviceIdleController.stepIdleStateLocked("testing");
                     count++;
                     if (count > 10) {
-                        fail(stateToString(mDeviceIdleController.getState()));
+                        fail("Infinite loop. Check test configuration. Currently at " +
+                                stateToString(mDeviceIdleController.getState()));
                     }
                 }
                 break;
@@ -943,6 +1401,8 @@
             case LIGHT_STATE_INACTIVE:
             case LIGHT_STATE_IDLE:
             case LIGHT_STATE_IDLE_MAINTENANCE:
+                // Start off from ACTIVE in case we're already past the desired state.
+                enterLightState(LIGHT_STATE_ACTIVE);
                 setScreenOn(false);
                 setChargingOn(false);
                 int count = 0;
@@ -954,7 +1414,8 @@
 
                     count++;
                     if (count > 10) {
-                        fail(lightStateToString(mDeviceIdleController.getLightState()));
+                        fail("Infinite loop. Check test configuration. Currently at " +
+                                lightStateToString(mDeviceIdleController.getLightState()));
                     }
                 }
                 break;
@@ -979,6 +1440,27 @@
         mDeviceIdleController.updateInteractivityLocked();
     }
 
+    private void setNetworkConnected(boolean connected) {
+        mInjector.connectivityService = mConnectivityService;
+        final NetworkInfo ani = mock(NetworkInfo.class);
+        doReturn(connected).when(ani).isConnected();
+        doReturn(ani).when(mConnectivityService).getActiveNetworkInfo();
+        mDeviceIdleController.updateConnectivityState(null);
+    }
+
+    private void setQuickDozeEnabled(boolean on) {
+        mDeviceIdleController.updateQuickDozeFlagLocked(on);
+    }
+
+    private void setAlarmSoon(boolean isSoon) {
+        if (isSoon) {
+            doReturn(SystemClock.elapsedRealtime() + mConstants.MIN_TIME_TO_ALARM / 2).when(
+                    mAlarmManager).getNextWakeFromIdleTime();
+        } else {
+            doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        }
+    }
+
     private void verifyStateConditions(int expectedState) {
         int curState = mDeviceIdleController.getState();
         assertEquals(
@@ -1015,7 +1497,9 @@
                 assertFalse(mDeviceIdleController.isScreenOn());
                 break;
             case STATE_IDLE:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertTrue(mDeviceIdleController.mMotionListener.isActive()
+                        // If quick doze is enabled, the motion listener should NOT be active.
+                        || mDeviceIdleController.isQuickDozeEnabled());
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
@@ -1023,7 +1507,16 @@
                 verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
                 break;
             case STATE_IDLE_MAINTENANCE:
-                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertTrue(mDeviceIdleController.mMotionListener.isActive()
+                        // If quick doze is enabled, the motion listener should NOT be active.
+                        || mDeviceIdleController.isQuickDozeEnabled());
+                assertFalse(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            case STATE_QUICK_DOZE_DELAY:
+                // If quick doze is enabled, the motion listener should NOT be active.
+                assertFalse(mDeviceIdleController.mMotionListener.isActive());
                 assertFalse(mAnyMotionDetector.isMonitoring);
                 assertFalse(mDeviceIdleController.isCharging());
                 assertFalse(mDeviceIdleController.isScreenOn());
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
index 26e77eb..8f39b4a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -32,9 +32,10 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Pair;
 
+import androidx.test.filters.SmallTest;
+
 import org.mockito.ArgumentMatchers;
 
 import java.util.ArrayList;
@@ -44,7 +45,7 @@
 @SmallTest
 public class PersistentConnectionTest extends AndroidTestCase {
     private static final String TAG = "PersistentConnectionTest";
-    
+
     private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
         public long uptimeMillis = 12345;
 
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
similarity index 78%
rename from services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
rename to services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 1752479..b63138e 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -16,19 +16,27 @@
 
 package com.android.server.job.controllers;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static org.junit.Assert.assertEquals;
 
 import android.app.job.JobInfo;
 import android.content.ComponentName;
+import android.content.pm.PackageManagerInternal;
 import android.os.SystemClock;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
 
 import java.time.Clock;
 import java.time.ZoneOffset;
@@ -37,8 +45,17 @@
 public class JobStatusTest {
     private static final double DELTA = 0.00001;
 
+    private MockitoSession mMockingSession;
+
     @Before
     public void setUp() throws Exception {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+        doReturn(mock(PackageManagerInternal.class))
+                .when(() -> LocalServices.getService(PackageManagerInternal.class));
+
         // Freeze the clocks at this moment in time
         JobSchedulerService.sSystemClock =
                 Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
@@ -48,6 +65,13 @@
                 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
     }
 
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
     @Test
     public void testFraction() throws Exception {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 2957267..878179b 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -24,7 +24,8 @@
     services.net \
     services.usage \
     guava \
-    androidx-test \
+    androidx.test.runner \
+    androidx.test.rules \
     mockito-target-minus-junit4 \
     platform-test-annotations \
     ShortcutManagerTestUtils \
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 1eb88ba..113ee2d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -26,13 +26,21 @@
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
+import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.Process.SYSTEM_UID;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -124,6 +132,7 @@
 import android.text.format.Time;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Range;
 import android.util.RecurrenceRule;
 
@@ -171,6 +180,7 @@
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Iterator;
@@ -1644,6 +1654,76 @@
                 true);
     }
 
+    /**
+     * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+     * conditions.
+     */
+    @Test
+    public void testIsUidNetworkingBlocked() {
+        final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
+
+        // Metered network. Data saver on.
+        expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+        expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+        verifyNetworkBlockedState(
+                true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+        expectedBlockedStates.clear();
+
+        // Metered network. Data saver off.
+        expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+        verifyNetworkBlockedState(
+                true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+        expectedBlockedStates.clear();
+
+        // Non-metered network. Data saver on.
+        expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
+        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+        verifyNetworkBlockedState(
+                false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+
+        // Non-metered network. Data saver off. The result is the same as previous case since
+        // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
+        verifyNetworkBlockedState(
+                false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+        expectedBlockedStates.clear();
+    }
+
+    private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
+            ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
+        final NetworkPolicyManagerInternal npmi = LocalServices
+                .getService(NetworkPolicyManagerInternal.class);
+
+        for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
+            final boolean expectedResult = pair.first;
+            final int rule = pair.second;
+            assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
+                    expectedResult,
+                    npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+            assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
+                    npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+        }
+    }
+
+    private String formatBlockedStateError(int uid, int rule, boolean metered,
+            boolean backgroundRestricted) {
+        return String.format(
+                "Unexpected BlockedState: (uid=%d, rule=%s, metered=%b, backgroundRestricted=%b)",
+                uid, uidRulesToString(rule), metered, backgroundRestricted);
+    }
+
     private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
         return SubscriptionPlan.Builder
                 .createRecurringMonthly(start)
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index 43438b9..8022532 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -124,7 +124,7 @@
                 "/storage/emulated/0/Android/sandbox/com.grey/foo.jpg",
                 "/storage/emulated/0/foo.jpg", PKG_GREY);
         assertTranslation(
-                "/storage/emulated/0/Android/sandbox/shared/colors/foo.jpg",
+                "/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
                 "/storage/emulated/0/foo.jpg", PKG_RED);
     }
 
@@ -134,7 +134,7 @@
                 "/storage/0000-0000/Android/sandbox/com.grey/foo/bar.jpg",
                 "/storage/0000-0000/foo/bar.jpg", PKG_GREY);
         assertTranslation(
-                "/storage/0000-0000/Android/sandbox/shared/colors/foo/bar.jpg",
+                "/storage/0000-0000/Android/sandbox/shared:colors/foo/bar.jpg",
                 "/storage/0000-0000/foo/bar.jpg", PKG_RED);
     }
 
@@ -147,7 +147,7 @@
 
         // Accessing other package paths goes into sandbox
         assertTranslation(
-                "/storage/emulated/0/Android/sandbox/shared/colors/"
+                "/storage/emulated/0/Android/sandbox/shared:colors/"
                         + "Android/data/com.grey/foo.jpg",
                 "/storage/emulated/0/Android/data/com.grey/foo.jpg", PKG_RED);
     }
@@ -192,7 +192,7 @@
         // Sandboxes can't see paths in other sandboxes
         try {
             mService.translateSystemToApp(
-                    "/storage/emulated/0/Android/sandbox/shared/colors/foo.jpg",
+                    "/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
                     PKG_GREY, UserHandle.USER_SYSTEM);
             fail();
         } catch (SecurityException expected) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index c88b873..feffeef 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -86,12 +86,8 @@
     final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
     final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
     final MessageCapturingHandler mMessageCapturingHandler =
-            new MessageCapturingHandler(new Handler.Callback() {
-        @Override
-        public boolean handleMessage(Message msg) {
-            return mMagnificationController.handleMessage(msg);
-        }
-    });
+            new MessageCapturingHandler(null);
+
     final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
     MagnificationController.SettingsBridge mMockSettingsBridge;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 79e4d70..032074a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -29,9 +29,11 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Message;
@@ -44,7 +46,9 @@
 
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
+import com.android.server.wm.WindowManagerInternal;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -101,8 +105,15 @@
     public static final float DEFAULT_Y = 299;
 
     private Context mContext;
-    private AccessibilityManagerService mAms;
-    private MagnificationController mMagnificationController;
+    final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
+    final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
+    final MessageCapturingHandler mMessageCapturingHandler =
+            new MessageCapturingHandler(null);
+    final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
+    MagnificationController.SettingsBridge mMockSettingsBridge =
+            mock(MagnificationController.SettingsBridge.class);
+    MagnificationController mMagnificationController;
+
     private OffsettableClock mClock;
     private MagnificationGestureHandler mMgh;
     private TestHandler mHandler;
@@ -112,9 +123,9 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getContext();
-        mAms = new AccessibilityManagerService(mContext);
-        mMagnificationController = new MagnificationController(
-                mContext, mAms, /* lock */ new Object()) {
+        mMagnificationController = new MagnificationController(mContext, mMockAms, new Object(),
+                mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator,
+                mMockSettingsBridge) {
             @Override
             public boolean magnificationRegionContains(float x, float y) {
                 return true;
@@ -123,7 +134,7 @@
             @Override
             void setForceShowMagnifiableBounds(boolean show) {}
         };
-        mMagnificationController.mRegistered = true;
+        mMagnificationController.register();
         mClock = new OffsettableClock.Stopped();
 
         boolean detectTripleTap = true;
@@ -131,6 +142,11 @@
         mMgh = newInstance(detectTripleTap, detectShortcutTrigger);
     }
 
+    @After
+    public void tearDown() {
+        mMagnificationController.unregister();
+    }
+
     @NonNull
     private MagnificationGestureHandler newInstance(boolean detectTripleTap,
             boolean detectShortcutTrigger) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
index ea90ffd..0da5742 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -20,11 +20,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
 
 import android.platform.test.annotations.Presubmit;
 
@@ -131,4 +134,57 @@
         new ActivityBuilder(mService).setTask(fullscreenTask).build();
         return fullscreenStack;
     }
+
+    /**
+     * Verifies the correct activity is returned when querying the top running activity.
+     */
+    @Test
+    public void testTopRunningActivity() {
+        // Create stack to hold focus.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        // Make sure the top running activity is not affected when keyguard is not locked.
+        assertTopRunningActivity(activity, display);
+
+        // Check to make sure activity not reported when it cannot show on lock and lock is on.
+        doReturn(true).when(keyguard).isKeyguardLocked();
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Change focus to stack with activity.
+        stack.moveToFront("focusChangeToTestStack");
+        assertEquals(stack, display.getFocusedStack());
+        assertEquals(activity, display.topRunningActivity());
+        assertNull(display.topRunningActivity(true /* considerKeyguardState */));
+
+        // Add activity that should be shown on the keyguard.
+        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(stack)
+                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
+                .build();
+
+        // Ensure the show when locked activity is returned.
+        assertTopRunningActivity(showWhenLockedActivity, display);
+
+        // Change focus back to empty stack.
+        emptyStack.moveToFront("focusChangeToEmptyStack");
+        assertEquals(emptyStack, display.getFocusedStack());
+        // If there is no running activity in focused stack, the running activity in next focusable
+        // stack should be returned.
+        assertTopRunningActivity(showWhenLockedActivity, display);
+    }
+
+    private static void assertTopRunningActivity(ActivityRecord top, ActivityDisplay display) {
+        assertEquals(top, display.topRunningActivity());
+        assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 9a7488e..8c27e25 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -133,7 +133,7 @@
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
             long lastNetworkUpdatedProcStateSeq) {
-        final UidRecord record = new UidRecord(uid);
+        final UidRecord record = new UidRecord(uid, null /* atmInternal */);
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
         record.waitingForNetwork = true;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index c44e492..349c0a37 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -64,6 +64,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -102,6 +103,7 @@
  *     com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner
  */
 @SmallTest
+@FlakyTest(bugId = 113616538)
 @RunWith(AndroidJUnit4.class)
 public class ActivityManagerServiceTest {
     private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
@@ -173,7 +175,7 @@
                 true); // expectNotify
 
         // Explicitly setting the seq counter for more verification.
-        mAms.mProcStateSeqCounter = 42;
+        mAms.mProcessList.mProcStateSeqCounter = 42;
 
         // Uid state is not moving from background to foreground or vice versa.
         verifySeqCounterAndInteractions(uidRec,
@@ -253,14 +255,14 @@
     }
 
     private UidRecord addUidRecord(int uid) {
-        final UidRecord uidRec = new UidRecord(uid);
+        final UidRecord uidRec = new UidRecord(uid, null /* atmInternal */);
         uidRec.waitingForNetwork = true;
         uidRec.hasInternetPermission = true;
         mAms.mActiveUids.put(uid, uidRec);
 
         final ProcessRecord appRec = new ProcessRecord(mAms, new ApplicationInfo(), TAG, uid, null);
         appRec.thread = Mockito.mock(IApplicationThread.class);
-        mAms.mLruProcesses.add(appRec);
+        mAms.mProcessList.mLruProcesses.add(appRec);
 
         return uidRec;
     }
@@ -272,14 +274,14 @@
         thread.startAndWait("Unexpected state for " + uidRec);
 
         uidRec.setProcState = prevState;
-        uidRec.curProcState = curState;
+        uidRec.setCurProcState(curState);
         mAms.incrementProcStateSeqAndNotifyAppsLocked();
 
-        assertEquals(expectedGlobalCounter, mAms.mProcStateSeqCounter);
+        assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
         assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
 
-        for (int i = mAms.mLruProcesses.size() - 1; i >= 0; --i) {
-            final ProcessRecord app = mAms.mLruProcesses.get(i);
+        for (int i = mAms.mProcessList.getLruSizeLocked() - 1; i >= 0; --i) {
+            final ProcessRecord app = mAms.mProcessList.mLruProcesses.get(i);
             // AMS should notify apps only for block states other than NETWORK_STATE_NO_CHANGE.
             if (app.uid == uidRec.uid && expectedBlockState == NETWORK_STATE_BLOCK) {
                 verify(app.thread).setNetworkBlockSeq(uidRec.curProcStateSeq);
@@ -299,7 +301,7 @@
 
     @Test
     public void testBlockStateForUid() {
-        final UidRecord uidRec = new UidRecord(TEST_UID);
+        final UidRecord uidRec = new UidRecord(TEST_UID, null /* atmInternal */);
         int expectedBlockState;
 
         final String errorTemplate = "Block state should be %s, prevState: %s, curState: %s";
@@ -307,47 +309,48 @@
             return String.format(errorTemplate,
                     valueToString(ActivityManagerService.class, "NETWORK_STATE_", blockState),
                     valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.setProcState),
-                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.curProcState));
+                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.getCurProcState())
+            );
         };
 
         // No change in uid state
         uidRec.setProcState = PROCESS_STATE_RECEIVER;
-        uidRec.curProcState = PROCESS_STATE_RECEIVER;
+        uidRec.setCurProcState(PROCESS_STATE_RECEIVER);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.getBlockStateForUid(uidRec));
 
         // Foreground to foreground
         uidRec.setProcState = PROCESS_STATE_FOREGROUND_SERVICE;
-        uidRec.curProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+        uidRec.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.getBlockStateForUid(uidRec));
 
         // Background to background
         uidRec.setProcState = PROCESS_STATE_CACHED_ACTIVITY;
-        uidRec.curProcState = PROCESS_STATE_CACHED_EMPTY;
+        uidRec.setCurProcState(PROCESS_STATE_CACHED_EMPTY);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.getBlockStateForUid(uidRec));
 
         // Background to background
         uidRec.setProcState = PROCESS_STATE_NONEXISTENT;
-        uidRec.curProcState = PROCESS_STATE_CACHED_ACTIVITY;
+        uidRec.setCurProcState(PROCESS_STATE_CACHED_ACTIVITY);
         expectedBlockState = NETWORK_STATE_NO_CHANGE;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.getBlockStateForUid(uidRec));
 
         // Background to foreground
         uidRec.setProcState = PROCESS_STATE_SERVICE;
-        uidRec.curProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+        uidRec.setCurProcState(PROCESS_STATE_FOREGROUND_SERVICE);
         expectedBlockState = NETWORK_STATE_BLOCK;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.getBlockStateForUid(uidRec));
 
         // Foreground to background
         uidRec.setProcState = PROCESS_STATE_TOP;
-        uidRec.curProcState = PROCESS_STATE_LAST_ACTIVITY;
+        uidRec.setCurProcState(PROCESS_STATE_LAST_ACTIVITY);
         expectedBlockState = NETWORK_STATE_UNBLOCK;
         assertEquals(errorMsg.apply(expectedBlockState),
                 expectedBlockState, mAms.getBlockStateForUid(uidRec));
@@ -591,10 +594,10 @@
                 assertNotNull("validateUidRecord should not be null since the change is neither "
                         + "CHANGE_GONE nor CHANGE_GONE_IDLE", validateUidRecord);
                 assertEquals("processState: " + item.processState + " curProcState: "
-                        + validateUidRecord.curProcState + " should have been equal",
-                        item.processState, validateUidRecord.curProcState);
+                        + validateUidRecord.getCurProcState() + " should have been equal",
+                        item.processState, validateUidRecord.getCurProcState());
                 assertEquals("processState: " + item.processState + " setProcState: "
-                        + validateUidRecord.curProcState + " should have been equal",
+                        + validateUidRecord.getCurProcState() + " should have been equal",
                         item.processState, validateUidRecord.setProcState);
                 if (item.change == UidRecord.CHANGE_IDLE) {
                     assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE",
@@ -624,7 +627,7 @@
 
     @Test
     public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID);
+        final UidRecord uidRecord = new UidRecord(TEST_UID, null /* atmInternal */);
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
 
         // Verify with no pending changes for TEST_UID.
@@ -670,7 +673,7 @@
     @MediumTest
     @Test
     public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID);
+        final UidRecord uidRecord = new UidRecord(TEST_UID, null /* atmInternal */);
         final int expectedProcState = PROCESS_STATE_SERVICE;
         uidRecord.setProcState = expectedProcState;
         uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
@@ -742,7 +745,7 @@
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
             long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {
-        final UidRecord record = new UidRecord(Process.myUid());
+        final UidRecord record = new UidRecord(Process.myUid(), null /* atmInternal */);
         record.curProcStateSeq = curProcStateSeq;
         record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
@@ -829,4 +832,4 @@
             mRestricted = restricted;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index ba25b16..2dfb375 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -16,38 +16,59 @@
 
 package com.android.server.am;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+
 import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
 import android.app.IActivityManager;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.RemoteException;
-import android.test.AndroidTestCase;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.List;
 
-public class ActivityManagerTest extends AndroidTestCase {
+import androidx.test.filters.FlakyTest;
 
-    IActivityManager service;
-    @Override
+/**
+ * Tests for {@link ActivityManager}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.am.ActivityManagerTest
+ */
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class ActivityManagerTest {
+
+    private IActivityManager service;
+
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         service = ActivityManager.getService();
     }
 
+    @Test
     public void testTaskIdsForRunningUsers() throws RemoteException {
-        for(int userId : service.getRunningUserIds()) {
+        int[] runningUserIds = service.getRunningUserIds();
+        assertThat(runningUserIds).isNotEmpty();
+        for (int userId : runningUserIds) {
             testTaskIdsForUser(userId);
         }
     }
 
     private void testTaskIdsForUser(int userId) throws RemoteException {
-        List<ActivityManager.RecentTaskInfo> recentTasks = service.getRecentTasks(
-                100, 0, userId).getList();
-        if(recentTasks != null) {
-            for(ActivityManager.RecentTaskInfo recentTask : recentTasks) {
-                int taskId = recentTask.persistentId;
+        List<?> recentTasks = service.getRecentTasks(100, 0, userId).getList();
+        if (recentTasks != null) {
+            for (Object elem : recentTasks) {
+                assertThat(elem).isInstanceOf(RecentTaskInfo.class);
+                RecentTaskInfo recentTask = (RecentTaskInfo) elem;
+                int taskId = recentTask.taskId;
                 assertEquals("The task id " + taskId + " should not belong to user " + userId,
-                        taskId / UserHandle.PER_USER_RANGE, userId);
+                             taskId / UserHandle.PER_USER_RANGE, userId);
             }
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 81a0934..2c993d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -18,13 +18,13 @@
 
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 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.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
 
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
@@ -34,9 +34,11 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 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.times;
@@ -304,62 +306,6 @@
     }
 
     /**
-     * Verifies the correct activity is returned when querying the top running activity.
-     */
-    @Test
-    public void testTopRunningActivity() throws Exception {
-        // Create stack to hold focus
-        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
-        final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        // Make sure the top running activity is not affected when keyguard is not locked
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Check to make sure activity not reported when it cannot show on lock and lock is on.
-        doReturn(true).when(keyguard).isKeyguardLocked();
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Change focus to stack with activity.
-        stack.moveToFront("focusChangeToTestStack");
-        assertEquals(stack, display.getFocusedStack());
-        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Add activity that should be shown on the keyguard.
-        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .setStack(stack)
-                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
-                .build();
-
-        // Ensure the show when locked activity is returned.
-        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-
-        // Change focus back to empty stack
-        emptyStack.moveToFront("focusChangeToEmptyStack");
-        assertEquals(emptyStack, display.getFocusedStack());
-        // Looking for running activity only in top and focused stack, so nothing should be returned
-        // from empty stack.
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked());
-        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
-                true /* considerKeyguardState */));
-    }
-
-    /**
      * Verify that split-screen primary stack will be chosen if activity is launched that targets
      * split-screen secondary, but a matching existing instance is found on top of split-screen
      * primary stack.
@@ -407,6 +353,57 @@
     }
 
     /**
+     * Verify that home stack would be moved to front when the top activity is Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnTop() throws Exception {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+        // Create Recents on top of the display.
+        final ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify that home stack won't be moved to front if the top activity on other display is
+     * Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() throws Exception {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+        // Create Recents on secondary display.
+        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+                ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display, never()).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
      * Verify if a stack is not at the topmost position, it should be able to resume its activity if
      * the stack is the top focused.
      */
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 5fcd2aa..53f67af 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -26,6 +26,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
@@ -35,10 +38,14 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.content.pm.ActivityInfo;
 import android.os.UserHandle;
@@ -655,6 +662,39 @@
     }
 
     @Test
+    public void testFinishCurrentActivity() {
+        // Create 2 activities on a new display.
+        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack1 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityStack stack2 = createStackForShouldBeVisibleTest(display,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        // There is still an activity1 in stack1 so the activity2 should be added to finishing list
+        // that will be destroyed until idle.
+        final ActivityRecord activity2 = finishCurrentActivity(stack2);
+        assertEquals(FINISHING, activity2.getState());
+        assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
+
+        // The display becomes empty. Since there is no next activity to be idle, the activity
+        // should be destroyed immediately with updating configuration to restore original state.
+        final ActivityRecord activity1 = finishCurrentActivity(stack1);
+        assertEquals(DESTROYING, activity1.getState());
+        verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+                eq(display.mDisplayId), anyBoolean(), anyBoolean());
+    }
+
+    private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+        final ActivityRecord activity = stack.topRunningActivityLocked();
+        assertNotNull(activity);
+        activity.setState(PAUSED, "finishCurrentActivity");
+        activity.makeFinishingLocked();
+        stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE,
+                false /* oomAdj */, "finishCurrentActivity");
+        return activity;
+    }
+
+    @Test
     public void testShouldSleepActivities() throws Exception {
         // When focused activity and keyguard is going away, we should not sleep regardless
         // of the display state
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 86541b9..270d394 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -35,6 +35,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -108,7 +109,6 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mService.mAm = mAm;
         mService.mAmInternal = mAmInternal;
         mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
@@ -165,17 +165,20 @@
     public void testSuspendedPackage() {
         mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
         final String suspendingPackage = "com.test.suspending.package";
-        final String dialogMessage = "Test Message";
+        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+                .setMessage("Test Message")
+                .setIcon(0x11110001)
+                .build();
         when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
                 .thenReturn(suspendingPackage);
-        when(mPackageManagerInternal.getSuspendedDialogMessage(TEST_PACKAGE_NAME, TEST_USER_ID))
-                .thenReturn(dialogMessage);
+        when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(dialogInfo);
         // THEN calling intercept returns true
         assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
 
         // Check intent parameters
-        assertEquals(dialogMessage,
-                mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE));
+        assertEquals(dialogInfo,
+                mInterceptor.mIntent.getParcelableExtra(SuspendedAppActivity.EXTRA_DIALOG_INFO));
         assertEquals(suspendingPackage,
                 mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE));
         assertEquals(TEST_PACKAGE_NAME,
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index bac4a52..ba64b51 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -37,7 +37,8 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 
 import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
-import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityDisplay.POSITION_TOP;
+import static com.android.server.am.ActivityTaskManagerService.ANIMATE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -48,6 +49,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -206,11 +208,11 @@
         prepareStarter(launchFlags);
         final IApplicationThread caller = mock(IApplicationThread.class);
 
-        // If no caller app, return {@code null} {@link ProcessRecord}.
-        final ProcessRecord record = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
-                ? null : new ProcessRecord(service.mAm, mock(ApplicationInfo.class), null, 0, null);
-
-        doReturn(record).when(service.mAm).getRecordForAppLocked(anyObject());
+        final WindowProcessController wpc =
+                containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
+                ? null : new WindowProcessController(
+                        service, mock(ApplicationInfo.class),null, 0, -1, null, null, null);
+        doReturn(wpc).when(service).getProcessController(anyObject());
 
         final Intent intent = new Intent();
         intent.setFlags(launchFlags);
@@ -354,10 +356,12 @@
                 invocation -> {
                     throw new RuntimeException("Not stubbed");
                 });
-        doReturn(mockPackageManager).when(mService.mAm).getPackageManagerInternalLocked();
+        doReturn(mockPackageManager).when(mService).getPackageManagerInternalLocked();
 
         // Never review permissions
         doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
+        doNothing().when(mockPackageManager).grantEphemeralAccess(
+                anyInt(), any(), anyInt(), anyInt());
 
         final Intent intent = new Intent();
         intent.addFlags(launchFlags);
@@ -408,8 +412,9 @@
                 .setActivityOptions(new SafeActivityOptions(options))
                 .execute();
 
-        // verify that values are passed to the modifier.
-        verify(modifier, times(1)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
+        // verify that values are passed to the modifier. Values are passed twice -- once for
+        // setting initial state, another when task is created.
+        verify(modifier, times(2)).onCalculate(any(), eq(windowLayout), any(), any(), eq(options),
                 any(), any());
     }
 
@@ -510,7 +515,7 @@
      */
     @Test
     public void testActivityStartsLogging_noLoggingWhenDisabled() {
-        doReturn(false).when(mService.mAm).isActivityStartsLoggingEnabled();
+        doReturn(false).when(mService).isActivityStartsLoggingEnabled();
         doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
 
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK);
@@ -528,7 +533,7 @@
     @Test
     public void testActivityStartsLogging_logsWhenEnabled() {
         // note: conveniently this package doesn't have any activity visible
-        doReturn(true).when(mService.mAm).isActivityStartsLoggingEnabled();
+        doReturn(true).when(mService).isActivityStartsLoggingEnabled();
         doReturn(mActivityMetricsLogger).when(mService.mStackSupervisor).getActivityMetricsLogger();
 
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
@@ -558,23 +563,13 @@
                 false /* mockGetLaunchStack */);
 
         // Create a secondary display at bottom.
-        final TestActivityDisplay secondaryDisplay = spy(addNewActivityDisplayAt(POSITION_BOTTOM));
+        final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+        mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
         final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Create an activity record on the top of secondary display.
-        final ComponentName componentName = ComponentName.createRelative(
-                DEFAULT_COMPONENT_PACKAGE_NAME,
-                DEFAULT_COMPONENT_PACKAGE_NAME + ".ReusableActivity");
-        final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
-                .setComponent(componentName)
-                .setStack(stack)
-                .build();
-        final ActivityRecord topActivityOnSecondaryDisplay = new ActivityBuilder(mService)
-                .setComponent(componentName)
-                .setLaunchMode(LAUNCH_SINGLE_TASK)
-                .setTask(taskRecord)
-                .build();
+        final ActivityRecord topActivityOnSecondaryDisplay = createSingleTaskActivityOn(stack);
 
         // Put an activity on default display as the top focused activity.
         new ActivityBuilder(mService).setCreateTask(true).build();
@@ -596,6 +591,59 @@
     }
 
     /**
+     * This test ensures that when starting an existing non-top single task activity on secondary
+     * display which is the top focused display, it should bring the task to front without creating
+     * unused stack.
+     */
+    @Test
+    public void testBringTaskToFrontOnSecondaryDisplay() {
+        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK,
+                false /* mockGetLaunchStack */);
+
+        // Create a secondary display with an activity.
+        final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
+        mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+        final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
+                secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                        ACTIVITY_TYPE_STANDARD, false /* onTop */));
+
+        // Create another activity on top of the secondary display.
+        final ActivityStack topStack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord topTask = new TaskBuilder(mSupervisor).setStack(topStack).build();
+        new ActivityBuilder(mService).setTask(topTask).build();
+
+        // Start activity with the same intent as {@code singleTaskActivity} on secondary display.
+        final ActivityOptions options = ActivityOptions.makeBasic()
+                .setLaunchDisplayId(secondaryDisplay.mDisplayId);
+        final int result = starter.setReason("testBringTaskToFrontOnSecondaryDisplay")
+                .setIntent(singleTaskActivity.intent)
+                .setActivityOptions(options.toBundle())
+                .execute();
+
+        // Ensure result is moving existing task to front.
+        assertEquals(START_TASK_TO_FRONT, result);
+
+        // Ensure secondary display only creates two stacks.
+        verify(secondaryDisplay, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
+    }
+
+    private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
+        final ComponentName componentName = ComponentName.createRelative(
+                DEFAULT_COMPONENT_PACKAGE_NAME,
+                DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
+        final TaskRecord taskRecord = new TaskBuilder(mSupervisor)
+                .setComponent(componentName)
+                .setStack(stack)
+                .build();
+        return new ActivityBuilder(mService)
+                .setComponent(componentName)
+                .setLaunchMode(LAUNCH_SINGLE_TASK)
+                .setTask(taskRecord)
+                .build();
+    }
+
+    /**
      * This test ensures that a reused top activity in the top focused stack is able to be
      * reparented to another display.
      */
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 2008861..01d51e4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -58,8 +58,9 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.HandlerThread;
+import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.testing.DexmakerShareClassLoaderRule;
@@ -69,7 +70,9 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.app.IVoiceInteractor;
+import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
+import com.android.server.ServiceThread;
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.RootWindowContainerController;
@@ -82,6 +85,7 @@
 import org.junit.Before;
 import org.mockito.MockitoAnnotations;
 
+import java.io.File;
 import java.util.List;
 
 /**
@@ -97,7 +101,7 @@
             new DexmakerShareClassLoaderRule();
 
     private final Context mContext = InstrumentationRegistry.getContext();
-    private HandlerThread mHandlerThread;
+    final TestInjector mTestInjector = new TestInjector();
 
     ActivityTaskManagerService mService;
     ActivityStackSupervisor mSupervisor;
@@ -115,13 +119,12 @@
             MockitoAnnotations.initMocks(this);
             AttributeCache.init(mContext);
         }
-        mHandlerThread = new HandlerThread("ActivityTestsBaseThread");
-        mHandlerThread.start();
+        mTestInjector.setUp();
     }
 
     @After
     public void tearDown() {
-        mHandlerThread.quitSafely();
+        mTestInjector.tearDown();
     }
 
     protected ActivityTaskManagerService createActivityTaskManagerService() {
@@ -143,7 +146,7 @@
     }
 
     ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        final TestActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
         setupActivityManagerService(am, atm);
         return am;
     }
@@ -162,7 +165,8 @@
 
     void setupActivityManagerService(
             TestActivityManagerService am, TestActivityTaskManagerService atm) {
-        atm.setActivityManagerService(am);
+        atm.setActivityManagerService(am, am.mHandlerThread.getLooper(), am.mIntentFirewall,
+                am.mPendingIntentController);
         atm.mAmInternal = am.getLocalService();
         am.mAtmInternal = atm.getLocalService();
         // Makes sure the supervisor is using with the spy object.
@@ -172,6 +176,7 @@
         doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
         doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
         doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
+        am.mActivityTaskManager = atm;
         am.mWindowManager = prepareMockWindowManager();
         atm.setWindowManager(am.mWindowManager);
 
@@ -191,8 +196,6 @@
         // An id appended to the end of the component name to make it unique
         private static int sCurrentActivityId = 0;
 
-
-
         private final ActivityTaskManagerService mService;
 
         private ComponentName mComponent;
@@ -486,6 +489,40 @@
         }
     }
 
+    private static class TestInjector extends ActivityManagerService.Injector {
+        private ServiceThread mHandlerThread;
+
+        @Override
+        public Context getContext() {
+            return InstrumentationRegistry.getContext();
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return null;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public boolean isNetworkRestrictedForUid(int uid) {
+            return false;
+        }
+
+        void setUp() {
+            mHandlerThread = new ServiceThread("ActivityTestsThread",
+                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+            mHandlerThread.start();
+        }
+
+        void tearDown() {
+            mHandlerThread.quitSafely();
+        }
+    }
+
     /**
      * An {@link ActivityManagerService} subclass which provides a test
      * {@link ActivityStackSupervisor}.
@@ -494,8 +531,8 @@
 
         private ActivityManagerInternal mInternal;
 
-        TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
-            super(context, atm);
+        TestActivityManagerService(TestInjector testInjector) {
+            super(testInjector, testInjector.mHandlerThread);
             mUgmInternal = mock(UriGrantsManagerInternal.class);
         }
 
@@ -545,6 +582,11 @@
         ActivityDisplay getDefaultDisplay() {
             return mDisplay;
         }
+
+        @Override
+        void setWindowManager(WindowManagerService wm) {
+            mWindowManager = wm;
+        }
     }
 
     protected static class TestActivityDisplay extends ActivityDisplay {
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
index 3819e21..06d41f1 100644
--- a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -21,6 +21,7 @@
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -37,6 +38,7 @@
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@FlakyTest(bugId = 113616538)
 public class AppErrorDialogTest {
 
     private Context mContext;
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index a030210..1b823ff 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -46,6 +46,7 @@
 import android.view.IWindowManager;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -67,6 +68,7 @@
  * runtest --path frameworks/base/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
  */
 @MediumTest
+@FlakyTest(bugId = 113616538)
 @RunWith(AndroidJUnit4.class)
 public class AssistDataRequesterTest extends ActivityTestsBase {
 
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 62c5734..75f7c4c 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -45,7 +45,7 @@
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class BroadcastRecordTest extends ActivityTestsBase {
+public class BroadcastRecordTest {
 
     @Test
     public void testCleanupDisabledPackageReceivers() {
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index fe8256e..719e0ed 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -132,15 +132,36 @@
                 settingsBundle.containsKey(TEST_SETTING_SYSTEM_STRING));
     }
 
+    @Test
+    public void testPopulateSettings_settingDeleted() {
+        Settings.Secure.putInt(mContentResolver, TEST_SETTING_SECURE_INT, TEST_INT);
+        Settings.Global.putFloat(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, TEST_FLOAT);
+        Settings.System.putString(mContentResolver, TEST_SETTING_SYSTEM_STRING, TEST_STRING);
+
+        Bundle settingsBundle = getPopulatedBundle();
+
+        assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
+                TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
+        assertEquals("Unexpected value of " + TEST_SETTING_GLOBAL_FLOAT,
+                TEST_FLOAT, settingsBundle.getFloat(TEST_SETTING_GLOBAL_FLOAT), 0);
+        assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
+                TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
+
+        Settings.Global.putString(mContentResolver, TEST_SETTING_GLOBAL_FLOAT, null);
+        settingsBundle = getPopulatedBundle();
+
+        assertFalse("Bundle should not contain " + TEST_SETTING_GLOBAL_FLOAT,
+                settingsBundle.containsKey(TEST_SETTING_GLOBAL_FLOAT));
+        assertEquals("Unexpected value of " + TEST_SETTING_SECURE_INT,
+                TEST_INT, settingsBundle.getInt(TEST_SETTING_SECURE_INT));
+        assertEquals("Unexpected value of " + TEST_SETTING_SYSTEM_STRING,
+                TEST_STRING, settingsBundle.getString(TEST_SETTING_SYSTEM_STRING));
+
+    }
+
     private Bundle getPopulatedBundle() {
-        final Bundle settingsBundle = new Bundle();
-        mCoreSettingsObserver.populateSettings(settingsBundle,
-                CoreSettingsObserver.sGlobalSettingToTypeMap);
-        mCoreSettingsObserver.populateSettings(settingsBundle,
-                CoreSettingsObserver.sSecureSettingToTypeMap);
-        mCoreSettingsObserver.populateSettings(settingsBundle,
-                CoreSettingsObserver.sSystemSettingToTypeMap);
-        return settingsBundle;
+        mCoreSettingsObserver.onChange(false);
+        return mCoreSettingsObserver.getCoreSettingsLocked();
     }
 
     private class TestInjector extends Injector {
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
index d4bab2e..2fb10e1 100644
--- a/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchParamsControllerTests.java
@@ -17,6 +17,8 @@
 package com.android.server.am;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
@@ -189,6 +191,35 @@
     }
 
     /**
+     * Tests preferred display id calculation for VR.
+     */
+    @Test
+    public void testVrPreferredDisplay() {
+        final int vr2dDisplayId = 1;
+        mService.mVr2dDisplayId = vr2dDisplayId;
+
+        final LaunchParams result = new LaunchParams();
+        final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
+        vrActivity.requestedVrComponent = vrActivity.realActivity;
+
+        // VR activities should always land on default display.
+        mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
+                null /*source*/, null /*options*/, result);
+        assertEquals(DEFAULT_DISPLAY, result.mPreferredDisplayId);
+
+        // Otherwise, always lands on VR 2D display.
+        final ActivityRecord vr2dActivity = new ActivityBuilder(mService).build();
+        mController.calculate(null /*task*/, null /*layout*/, vr2dActivity /*activity*/,
+                null /*source*/, null /*options*/, result);
+        assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
+        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
+                null /*options*/, result);
+        assertEquals(vr2dDisplayId, result.mPreferredDisplayId);
+
+        mService.mVr2dDisplayId = INVALID_DISPLAY;
+    }
+
+    /**
      * Ensures that {@link LaunchParamsModifier} requests specifying display id during
      * layout are honored.
      */
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 1276f65..27e8c63 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -116,8 +116,7 @@
 
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
         mService = spy(new MyTestActivityTaskManagerService(mContext));
-        final TestActivityManagerService am =
-                spy(new MyTestActivityManagerService(mContext, mService));
+        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
         setupActivityManagerService(am, mService);
         mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
         mRecentTasks.loadParametersFromResources(mContext.getResources());
@@ -848,8 +847,8 @@
     }
 
     private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
-            super(context, atm);
+        MyTestActivityManagerService() {
+            super(mTestInjector);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
index 0d1302f..169204f 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskLaunchParamsModifierTests.java
@@ -29,6 +29,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
+import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -97,6 +98,13 @@
         mResult.reset();
     }
 
+    @Test
+    public void testReturnsSkipWithEmptyActivity() {
+        final TaskRecord task = new TaskBuilder(mSupervisor).build();
+        assertEquals(RESULT_SKIP, mTarget.onCalculate(task, /* layout */ null,
+                /* activity */ null, /* source */ null, /* options */ null, mCurrent, mResult));
+    }
+
     // =============================
     // Display ID Related Tests
     // =============================
@@ -189,7 +197,7 @@
         assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
                 mActivity, /* source */ null, options, mCurrent, mResult));
 
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
                 WINDOWING_MODE_FULLSCREEN);
     }
 
@@ -277,7 +285,7 @@
     }
 
     @Test
-    public void testNonEmptyLayoutInfersFreeformWithResizeableActivity() {
+    public void testNonEmptyLayoutUsesFullscreenWithResizeableActivity() {
         final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
                 .setWidth(120).setHeight(80).build();
 
@@ -286,7 +294,7 @@
         assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
                 /* source */ null, /* options */ null, mCurrent, mResult));
 
-        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
                 WINDOWING_MODE_FULLSCREEN);
     }
 
@@ -713,21 +721,6 @@
     }
 
     @Test
-    public void testNonEmptyLayoutBoundsWithResizeableActivity() {
-        final ActivityDisplay display = mSupervisor.getActivityDisplay(DEFAULT_DISPLAY);
-        display.setBounds(new Rect(0, 0, 1920, 1080));
-        final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
-                .setWidth(120).setHeight(80).build();
-
-        mCurrent.mPreferredDisplayId = DEFAULT_DISPLAY;
-
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, layout, mActivity,
-                /* source */ null, /* options */ null, mCurrent, mResult));
-
-        assertEquals(new Rect(900, 500, 1020, 580), mResult.mBounds);
-    }
-
-    @Test
     public void testRespectBoundsFromFullyResolvedCurrentParam_NonEmptyBounds() {
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
index 8d54bc2..48bfe1d 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java
@@ -16,34 +16,43 @@
 
 package com.android.server.am;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
 import android.content.pm.UserInfo;
-import android.os.Environment;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.test.AndroidTestCase;
-import android.util.Log;
+import android.platform.test.annotations.Presubmit;
 import android.util.SparseBooleanArray;
 
-import com.android.server.am.TaskPersister;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
-import java.io.File;
-import java.util.Random;
+import androidx.test.filters.FlakyTest;
 
 /**
- * atest FrameworksServicesTests:TaskPersisterTest
+ * Tests for {@link TaskPersister}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:TaskPersisterTest
  */
-public class TaskPersisterTest extends AndroidTestCase {
+@Presubmit
+@FlakyTest(detail = "Promote to presubmit if stable")
+public class TaskPersisterTest {
     private static final String TEST_USER_NAME = "AM-Test-User";
 
     private TaskPersister mTaskPersister;
     private int testUserId;
     private UserManager mUserManager;
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-        mUserManager = UserManager.get(getContext());
-        mTaskPersister = new TaskPersister(getContext().getFilesDir());
+        mUserManager = UserManager.get(getTargetContext());
+        mTaskPersister = new TaskPersister(getTargetContext().getFilesDir());
         // In ARC, the maximum number of supported users is one, which is different from the ones of
         // most phones (more than 4). This prevents TaskPersisterTest from creating another user for
         // test. However, since guest users can be added as much as possible, we create guest user
@@ -51,9 +60,8 @@
         testUserId = createUser(TEST_USER_NAME, UserInfo.FLAG_GUEST);
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
-        super.tearDown();
         mTaskPersister.unloadUserDataFromMemory(testUserId);
         removeUser(testUserId);
     }
@@ -64,6 +72,7 @@
         return taskId;
     }
 
+    @Test
     public void testTaskIdsPersistence() {
         SparseBooleanArray taskIdsOnFile = new SparseBooleanArray();
         for (int i = 0; i < 100; i++) {
@@ -72,21 +81,18 @@
         mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, testUserId);
         SparseBooleanArray newTaskIdsOnFile = mTaskPersister
                 .loadPersistedTaskIdsForUser(testUserId);
-        assertTrue("TaskIds written differ from TaskIds read back from file",
-                taskIdsOnFile.equals(newTaskIdsOnFile));
+        assertEquals("TaskIds written differ from TaskIds read back from file",
+                taskIdsOnFile, newTaskIdsOnFile);
     }
 
     private int createUser(String name, int flags) {
         UserInfo user = mUserManager.createUser(name, flags);
-        if (user == null) {
-            fail("Error while creating the test user: " + TEST_USER_NAME);
-        }
+        assertNotNull("Error while creating the test user: " + TEST_USER_NAME, user);
         return user.id;
     }
 
     private void removeUser(int userId) {
-        if (!mUserManager.removeUser(userId)) {
-            fail("Error while removing the test user: " + TEST_USER_NAME);
-        }
+        boolean userRemoved = mUserManager.removeUser(userId);
+        assertTrue("Error while removing the test user: " + TEST_USER_NAME, userRemoved);
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index fa8a09c..27766d3 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -69,13 +69,11 @@
 
     private static final String TASK_TAG = "task";
 
-    private ActivityManagerService mService;
-
     @Before
     public void setUp() throws Exception {
         super.setUp();
         TaskRecord.setTaskRecordFactory(null);
-        mService = createActivityManagerService();
+        setupActivityTaskManagerService();
     }
 
     @Test
@@ -150,7 +148,7 @@
     }
 
     private TaskRecord createTaskRecord(int taskId) {
-        return new TaskRecord(mService.mActivityTaskManager, taskId, new Intent(), null, null, null,
+        return new TaskRecord(mService, taskId, new Intent(), null, null, null,
                 ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
                 new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0, 0
         );
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index cc4f519..75e1d0d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,41 @@
 
 package com.android.server.am;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
+import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
+import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
+import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
+
+import static com.google.android.collect.Lists.newArrayList;
+import static com.google.android.collect.Sets.newHashSet;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.validateMockitoUsage;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
 import android.app.IUserSwitchObserver;
 import android.content.Context;
 import android.content.IIntentReceiver;
@@ -31,80 +66,62 @@
 import android.os.RemoteException;
 import android.os.UserManagerInternal;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.WindowManagerService;
 
-import org.mockito.Mockito;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG;
-import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG;
-import static com.android.server.am.UserController.SYSTEM_USER_START_MSG;
-import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.when;
+import androidx.test.filters.SmallTest;
 
 /**
- * Usage: bit FrameworksServicesTests:com.android.server.am.UserControllerTest
+ * Tests for {@link UserController}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.am.UserControllerTest
  */
 @Presubmit
-public class UserControllerTest extends AndroidTestCase {
+@SmallTest
+public class UserControllerTest {
     private static final int TEST_USER_ID = 10;
     private static final int NONEXIST_USER_ID = 2;
     private static String TAG = UserControllerTest.class.getSimpleName();
     private UserController mUserController;
     private TestInjector mInjector;
 
-    private static final List<String> START_FOREGROUND_USER_ACTIONS =
-            Arrays.asList(
-                    Intent.ACTION_USER_STARTED,
-                    Intent.ACTION_USER_SWITCHED,
-                    Intent.ACTION_USER_STARTING);
+    private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
+            Intent.ACTION_USER_STARTED,
+            Intent.ACTION_USER_SWITCHED,
+            Intent.ACTION_USER_STARTING);
 
-    private static final List<String> START_BACKGROUND_USER_ACTIONS =
-            Arrays.asList(
-                    Intent.ACTION_USER_STARTED,
-                    Intent.ACTION_LOCKED_BOOT_COMPLETED,
-                    Intent.ACTION_USER_STARTING);
+    private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
+            Intent.ACTION_USER_STARTED,
+            Intent.ACTION_LOCKED_BOOT_COMPLETED,
+            Intent.ACTION_USER_STARTING);
 
-    private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES =
-            new HashSet<>(Arrays.asList(REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG,
-                    SYSTEM_USER_START_MSG, SYSTEM_USER_CURRENT_MSG));
+    private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
+            REPORT_USER_SWITCH_MSG,
+            USER_SWITCH_TIMEOUT_MSG,
+            SYSTEM_USER_START_MSG,
+            SYSTEM_USER_CURRENT_MSG);
 
-    private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES =
-            new HashSet<>(Arrays.asList(SYSTEM_USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG));
+    private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
+            SYSTEM_USER_START_MSG,
+            REPORT_LOCKED_BOOT_COMPLETE_MSG);
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
         runWithDexmakerShareClassLoader(() -> {
-            mInjector = Mockito.spy(new TestInjector(getContext()));
+            mInjector = spy(new TestInjector(getTargetContext()));
             doNothing().when(mInjector).clearAllLockedTasks(anyString());
             doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
             doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any());
@@ -114,58 +131,54 @@
         });
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         mInjector.handlerThread.quit();
-        Mockito.validateMockitoUsage();
+        validateMockitoUsage();
     }
 
-    @SmallTest
-    public void testStartUser_foreground() throws RemoteException {
+    @Test
+    public void testStartUser_foreground() {
         mUserController.startUser(TEST_USER_ID, true /* foreground */);
-        Mockito.verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
-        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
-        Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
-        Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(true);
-        Mockito.verify(mInjector).clearAllLockedTasks(anyString());
+        verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+        verify(mInjector.getWindowManager()).setSwitchingUser(true);
+        verify(mInjector).clearAllLockedTasks(anyString());
         startForegroundUserAssertions();
     }
 
-    @SmallTest
-    public void testStartUser_background() throws RemoteException {
+    @Test
+    public void testStartUser_background() {
         mUserController.startUser(TEST_USER_ID, false /* foreground */);
-        Mockito.verify(
-                mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
-        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
-        Mockito.verify(mInjector, never()).clearAllLockedTasks(anyString());
+        verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+        verify(mInjector, never()).clearAllLockedTasks(anyString());
         startBackgroundUserAssertions();
     }
 
-    @SmallTest
-    public void testStartUserUIDisabled() throws RemoteException {
+    @Test
+    public void testStartUserUIDisabled() {
         mUserController.mUserSwitchUiEnabled = false;
         mUserController.startUser(TEST_USER_ID, true /* foreground */);
-        Mockito.verify(mInjector.getWindowManager(), never())
-                .startFreezingScreen(anyInt(), anyInt());
-        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
-        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
+        verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+        verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
         startForegroundUserAssertions();
     }
 
     private void startUserAssertions(
-            List<String> expectedActions, Set<Integer> expectedMessageCodes)
-            throws RemoteException {
+            List<String> expectedActions, Set<Integer> expectedMessageCodes) {
         assertEquals(expectedActions, getActions(mInjector.sentIntents));
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedMessageCodes, actualCodes);
     }
 
-    private void startBackgroundUserAssertions() throws RemoteException {
+    private void startBackgroundUserAssertions() {
         startUserAssertions(START_BACKGROUND_USER_ACTIONS, START_BACKGROUND_USER_MESSAGE_CODES);
     }
 
-    private void startForegroundUserAssertions() throws RemoteException {
+    private void startForegroundUserAssertions() {
         startUserAssertions(START_FOREGROUND_USER_ACTIONS, START_FOREGROUND_USER_MESSAGE_CODES);
         Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
@@ -177,15 +190,15 @@
         assertEquals("Unexpected new user id", TEST_USER_ID, reportMsg.arg2);
     }
 
-    @SmallTest
-    public void testFailedStartUserInForeground() throws RemoteException {
+    @Test
+    public void testFailedStartUserInForeground() {
         mUserController.mUserSwitchUiEnabled = false;
         mUserController.startUserInForeground(NONEXIST_USER_ID);
-        Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
-        Mockito.verify(mInjector.getWindowManager()).setSwitchingUser(false);
+        verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
+        verify(mInjector.getWindowManager()).setSwitchingUser(false);
     }
 
-    @SmallTest
+    @Test
     public void testDispatchUserSwitch() throws RemoteException {
         // Prepare mock observer and register it
         IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -206,7 +219,7 @@
         // Call dispatchUserSwitch and verify that observer was called only once
         mInjector.handler.clearAllRecordedMessages();
         mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+        verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
         Set<Integer> expectedCodes = Collections.singleton(CONTINUE_USER_SWITCH_MSG);
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -220,7 +233,7 @@
         assertEquals("Unexpected new user id", TEST_USER_ID, conMsg.arg2);
     }
 
-    @SmallTest
+    @Test
     public void testDispatchUserSwitchBadReceiver() throws RemoteException {
         // Prepare mock observer which doesn't notify the callback and register it
         IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -236,14 +249,14 @@
         // Call dispatchUserSwitch and verify that observer was called only once
         mInjector.handler.clearAllRecordedMessages();
         mUserController.dispatchUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
+        verify(observer, times(1)).onUserSwitching(eq(TEST_USER_ID), any());
         // Verify that CONTINUE_USER_SWITCH_MSG is not sent (triggers timeout)
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
-        assertTrue("No messages should be sent", actualCodes.isEmpty());
+        assertWithMessage("No messages should be sent").that(actualCodes).isEmpty();
     }
 
-    @SmallTest
-    public void testContinueUserSwitch() throws RemoteException {
+    @Test
+    public void testContinueUserSwitch() {
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
         Message reportMsg = mInjector.handler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -254,12 +267,12 @@
         mInjector.handler.clearAllRecordedMessages();
         // Verify that continueUserSwitch worked as expected
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
         continueUserSwitchAssertions();
     }
 
-    @SmallTest
-    public void testContinueUserSwitchUIDisabled() throws RemoteException {
+    @Test
+    public void testContinueUserSwitchUIDisabled() {
         mUserController.mUserSwitchUiEnabled = false;
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, true);
@@ -271,11 +284,11 @@
         mInjector.handler.clearAllRecordedMessages();
         // Verify that continueUserSwitch worked as expected
         mUserController.continueUserSwitch(userState, oldUserId, newUserId);
-        Mockito.verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+        verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
         continueUserSwitchAssertions();
     }
 
-    private void continueUserSwitchAssertions() throws RemoteException {
+    private void continueUserSwitchAssertions() {
         Set<Integer> expectedCodes = Collections.singleton(REPORT_USER_SWITCH_COMPLETE_MSG);
         Set<Integer> actualCodes = mInjector.handler.getMessageCodes();
         assertEquals("Unexpected message sent", expectedCodes, actualCodes);
@@ -284,7 +297,7 @@
         assertEquals("Unexpected userId", TEST_USER_ID, msg.arg1);
     }
 
-    @SmallTest
+    @Test
     public void testDispatchUserSwitchComplete() throws RemoteException {
         // Prepare mock observer and register it
         IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
@@ -298,12 +311,12 @@
         mInjector.handler.clearAllRecordedMessages();
         // Mockito can't reset only interactions, so just verify that this hasn't been
         // called with 'false' until after dispatchUserSwitchComplete.
-        Mockito.verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
+        verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
         // Call dispatchUserSwitchComplete
         mUserController.dispatchUserSwitchComplete(newUserId);
-        Mockito.verify(observer, times(1)).onUserSwitchComplete(anyInt());
-        Mockito.verify(observer).onUserSwitchComplete(TEST_USER_ID);
-        Mockito.verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+        verify(observer, times(1)).onUserSwitchComplete(anyInt());
+        verify(observer).onUserSwitchComplete(TEST_USER_ID);
+        verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
     }
 
     private void setUpUser(int userId, int flags) {
@@ -320,7 +333,7 @@
     }
 
     // Should be public to allow mocking
-    public static class TestInjector extends UserController.Injector {
+    private static class TestInjector extends UserController.Injector {
         TestHandler handler;
         TestHandler uiHandler;
         HandlerThread handlerThread;
@@ -438,4 +451,4 @@
             return super.sendMessageAtTime(msg, uptimeMillis);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 79eba68..92211ec 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -887,7 +887,7 @@
         }
 
         @Override
-        protected BackupManagerServiceInterface createBackupManagerService() {
+        protected BackupManagerService createBackupManagerService() {
             mCreateServiceCallsCount++;
             return sBackupManagerServiceMock;
         }
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
new file mode 100644
index 0000000..0328621
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.inputmethod;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodManagerServiceTests {
+    static final int SYSTEM_DECORATION_SUPPORT_DISPLAY_ID = 2;
+    static final int NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID = 3;
+
+    static InputMethodManagerService.ImeDisplayValidator sChecker =
+            (displayId) -> {
+                switch (displayId) {
+                    case SYSTEM_DECORATION_SUPPORT_DISPLAY_ID:
+                        return true;
+                    case NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID:
+                        return false;
+                    default:
+                        throw new IllegalArgumentException("Unknown displayId=" + displayId);
+                }
+            };
+
+    static InputMethodManagerService.ImeDisplayValidator sMustNotBeCalledChecker =
+            (displayId) -> {
+                fail("Should not pass to display config check for this test case.");
+                return false;
+            };
+
+    @Test
+    public void testComputeImeDisplayId_defaultDisplayId() {
+        // Make sure that there is a short-circuit for DEFAULT_DISPLAY.
+        assertEquals(DEFAULT_DISPLAY,
+                InputMethodManagerService.computeImeDisplayIdForTarget(
+                        DEFAULT_DISPLAY, false /* isVrImeStarted */,
+                        sMustNotBeCalledChecker));
+    }
+
+    @Test
+    public void testComputeImeDisplayId_InvalidDisplayId() {
+        // Make sure that there is a short-circuit for INVALID_DISPLAY.
+        assertEquals(DEFAULT_DISPLAY,
+                InputMethodManagerService.computeImeDisplayIdForTarget(
+                        INVALID_DISPLAY, false /* isVrImeStarted */,
+                        sMustNotBeCalledChecker));
+    }
+
+    @Test
+    public void testComputeImeDisplayId_VrIme() {
+        // Make sure that there is a short-circuit for VR IME.
+        assertEquals(DEFAULT_DISPLAY,
+                InputMethodManagerService.computeImeDisplayIdForTarget(
+                        SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, true /* isVrImeStarted */,
+                        sMustNotBeCalledChecker));
+    }
+
+    @Test
+    public void testComputeImeDisplayId_noSystemDecorationSupportDisplay() {
+        // Presume display didn't support system decoration.
+        // Make sure IME displayId is DEFAULT_DISPLAY.
+        assertEquals(DEFAULT_DISPLAY,
+                InputMethodManagerService.computeImeDisplayIdForTarget(
+                        NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
+                        sChecker));
+    }
+
+    @Test
+    public void testComputeImeDisplayId_withSystemDecorationSupportDisplay() {
+        // Presume display support system decoration.
+        // Make sure IME displayId is the same display.
+        assertEquals(SYSTEM_DECORATION_SUPPORT_DISPLAY_ID,
+                InputMethodManagerService.computeImeDisplayIdForTarget(
+                        SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
+                        sChecker));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index c3c0788..517b5ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -37,6 +37,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
+import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
 import android.os.BaseBundle;
 import android.os.PersistableBundle;
@@ -200,13 +201,21 @@
                 PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
         final PersistableBundle launcherExtras1 = getPersistableBundle(
                 PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
-        ps1.setSuspended(true, "suspendingPackage1", "dialogMsg1", appExtras1, launcherExtras1, 0);
+
+        final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
+                .setIcon(0x11220001)
+                .setTitle(0x11220002)
+                .setMessage("1st message")
+                .setNeutralButtonText(0x11220003)
+                .build();
+
+        ps1.setSuspended(true, "suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1, 0);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
 
-        ps2.setSuspended(true, "suspendingPackage2", "dialogMsg2", null, null, 0);
+        ps2.setSuspended(true, "suspendingPackage2", null, null, null, 0);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
 
-        ps3.setSuspended(false, "irrelevant", "irrevelant2", null, null, 0);
+        ps3.setSuspended(false, "irrelevant", dialogInfo1, null, null, 0);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
 
         settingsUnderTest.writePackageRestrictionsLPr(0);
@@ -221,7 +230,7 @@
                 readUserState(0);
         assertThat(readPus1.suspended, is(true));
         assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
-        assertThat(readPus1.dialogMessage, equalTo("dialogMsg1"));
+        assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
         assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
         assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
                 is(true));
@@ -230,7 +239,7 @@
                 readUserState(0);
         assertThat(readPus2.suspended, is(true));
         assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
-        assertThat(readPus2.dialogMessage, equalTo("dialogMsg2"));
+        assertThat(readPus2.dialogInfo, is(nullValue()));
         assertThat(readPus2.suspendedAppExtras, is(nullValue()));
         assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
 
@@ -238,7 +247,7 @@
                 readUserState(0);
         assertThat(readPus3.suspended, is(false));
         assertThat(readPus3.suspendingPackage, is(nullValue()));
-        assertThat(readPus3.dialogMessage, is(nullValue()));
+        assertThat(readPus3.dialogInfo, is(nullValue()));
         assertThat(readPus3.suspendedAppExtras, is(nullValue()));
         assertThat(readPus3.suspendedLauncherExtras, is(nullValue()));
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 4a33ca3..f0ed612 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertThat;
 
 import android.content.pm.PackageUserState;
+import android.content.pm.SuspendDialogInfo;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
 
@@ -37,7 +38,7 @@
 public class PackageUserStateTest {
 
     @Test
-    public void testPackageUserState01()  {
+    public void testPackageUserState01() {
         final PackageUserState testUserState = new PackageUserState();
         PackageUserState oldUserState;
 
@@ -84,7 +85,7 @@
     }
 
     @Test
-    public void testPackageUserState02()  {
+    public void testPackageUserState02() {
         final PackageUserState testUserState01 = new PackageUserState();
         PackageUserState oldUserState;
 
@@ -102,7 +103,7 @@
     }
 
     @Test
-    public void testPackageUserState03()  {
+    public void testPackageUserState03() {
         final PackageUserState oldUserState = new PackageUserState();
 
         // only new user state has array defined; different
@@ -138,7 +139,7 @@
     }
 
     @Test
-    public void testPackageUserState04()  {
+    public void testPackageUserState04() {
         final PackageUserState oldUserState = new PackageUserState();
 
         // only new user state has array defined; different
@@ -185,15 +186,19 @@
         launcherExtras2.putString("name", "launcherExtras2");
         final String suspendingPackage1 = "package1";
         final String suspendingPackage2 = "package2";
-        final String dialogMessage1 = "dialogMessage1";
-        final String dialogMessage2 = "dialogMessage2";
+        final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
+                .setMessage("dialogMessage1")
+                .build();
+        final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder()
+                .setMessage("dialogMessage2")
+                .build();
 
         final PackageUserState testUserState1 = new PackageUserState();
         testUserState1.suspended = true;
         testUserState1.suspendedAppExtras = appExtras1;
         testUserState1.suspendedLauncherExtras = launcherExtras1;
         testUserState1.suspendingPackage = suspendingPackage1;
-        testUserState1.dialogMessage = dialogMessage1;
+        testUserState1.dialogInfo = dialogInfo1;
 
         PackageUserState testUserState2 = new PackageUserState(testUserState1);
         assertThat(testUserState1.equals(testUserState2), is(true));
@@ -209,14 +214,14 @@
         assertThat(testUserState1.equals(testUserState2), is(false));
 
         testUserState2 = new PackageUserState(testUserState1);
-        testUserState2.dialogMessage = dialogMessage2;
+        testUserState2.dialogInfo = dialogInfo2;
         assertThat(testUserState1.equals(testUserState2), is(false));
 
         testUserState2 = new PackageUserState(testUserState1);
         testUserState2.suspended = testUserState1.suspended = false;
         // Everything is different but irrelevant if suspended is false
         testUserState2.suspendingPackage = suspendingPackage2;
-        testUserState2.dialogMessage = dialogMessage2;
+        testUserState2.dialogInfo = dialogInfo2;
         testUserState2.suspendedAppExtras = appExtras2;
         testUserState2.suspendedLauncherExtras = launcherExtras2;
         assertThat(testUserState1.equals(testUserState2), is(true));
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
new file mode 100644
index 0000000..7eccd67
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendDialogInfoTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.pm.SuspendDialogInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SuspendDialogInfoTest {
+    private static final int VALID_TEST_RES_ID_1 = 0x11110001;
+    private static final int VALID_TEST_RES_ID_2 = 0x11110002;
+
+    private static SuspendDialogInfo.Builder createDefaultDialogBuilder() {
+        return new SuspendDialogInfo.Builder()
+                .setIcon(VALID_TEST_RES_ID_1)
+                .setTitle(VALID_TEST_RES_ID_1)
+                .setMessage(VALID_TEST_RES_ID_1)
+                .setNeutralButtonText(VALID_TEST_RES_ID_1);
+    }
+
+    @Test
+    public void equalsComparesIcons() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+        final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+        // Only icon is different
+        dialogBuilder2.setIcon(VALID_TEST_RES_ID_2);
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsComparesTitle() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+        final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+        // Only title is different
+        dialogBuilder2.setTitle(VALID_TEST_RES_ID_2);
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsComparesButtonText() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+        final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+        // Only button text is different
+        dialogBuilder2.setNeutralButtonText(VALID_TEST_RES_ID_2);
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsComparesMessageIds() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
+        final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+        // Only message is different
+        dialogBuilder2.setMessage(VALID_TEST_RES_ID_2);
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsIgnoresMessageStringsWhenIdsSet() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+                .setMessage(VALID_TEST_RES_ID_1)
+                .setMessage("1st message");
+        final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+                .setMessage(VALID_TEST_RES_ID_1)
+                .setMessage("2nd message");
+        // String messages different but should get be ignored when resource ids are set
+        assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void equalsComparesMessageStringsWhenNoIdsSet() {
+        final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
+                .setMessage("1st message");
+        final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
+                .setMessage("2nd message");
+        // Both have different messages, which are not ignored as resource ids aren't set
+        assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
+    }
+
+    @Test
+    public void messageStringClearedWhenResIdSet() {
+        final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+                .setMessage(VALID_TEST_RES_ID_2)
+                .setMessage("Should be cleared on build")
+                .build();
+        assertNull(dialogInfo.getDialogMessage());
+        assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getDialogMessageResId());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index f115b9c..553d234 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -33,6 +33,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
+import android.content.pm.SuspendDialogInfo;
 import android.content.res.Resources;
 import android.os.BaseBundle;
 import android.os.Bundle;
@@ -152,7 +153,7 @@
         }
 
         void drainPendingBroadcasts() {
-            while (pollForIntent(5) != null);
+            while (pollForIntent(5) != null) ;
         }
 
         Intent receiveIntentFromApp() {
@@ -215,15 +216,15 @@
     }
 
     private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras,
-            String dialogMessage) {
+            SuspendDialogInfo dialogInfo) {
         final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
-                PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogMessage);
+                PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogInfo);
         assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
     }
 
     private void unsuspendTestPackage() {
         final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
-                PACKAGES_TO_SUSPEND, false, null, null, null);
+                PACKAGES_TO_SUSPEND, false, null, null, (SuspendDialogInfo) null);
         assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
     }
 
@@ -318,7 +319,8 @@
     @Test
     public void testCannotSuspendSelf() {
         final String[] unchangedPkgs = mPackageManager.setPackagesSuspended(
-                new String[]{mContext.getOpPackageName()}, true, null, null, null);
+                new String[]{mContext.getOpPackageName()}, true, null, null,
+                (SuspendDialogInfo) null);
         assertTrue(unchangedPkgs.length == 1);
         assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]);
     }
@@ -457,7 +459,8 @@
         mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED,
                 ACTION_REPORT_TEST_ACTIVITY_STARTED);
         final String testMessage = "This is a test message to report suspension of %1$s";
-        suspendTestPackage(null, null, testMessage);
+        suspendTestPackage(null, null,
+                new SuspendDialogInfo.Builder().setMessage(testMessage).build());
         startTestAppActivity();
         assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2));
         assertNotNull("Given dialog message not shown", mUiDevice.wait(
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index bd4a356..77f6a23 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -58,7 +58,8 @@
             + "adjust_brightness_factor=0.7,"
             + "fullbackup_deferred=true,"
             + "keyvaluebackup_deferred=false,"
-            + "gps_mode=0";
+            + "gps_mode=0,"
+            + "quick_doze_enabled=true";
     private static final String BATTERY_SAVER_INCORRECT_CONSTANTS = "vi*,!=,,true";
 
     private class BatterySaverPolicyForTest extends BatterySaverPolicy {
@@ -102,48 +103,48 @@
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyNull_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.NULL);
+        testServiceDefaultValue_On(ServiceType.NULL);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyVibration_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.VIBRATION);
+        testServiceDefaultValue_On(ServiceType.VIBRATION);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyVibration_WithAccessibilityEnabled() {
         mBatterySaverPolicy.setAccessibilityEnabledForTest(true);
-        testServiceDefaultValue_unchanged(ServiceType.VIBRATION);
+        testServiceDefaultValue_Off(ServiceType.VIBRATION);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicySound_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.SOUND);
+        testServiceDefaultValue_On(ServiceType.SOUND);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyFullBackup_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.FULL_BACKUP);
+        testServiceDefaultValue_On(ServiceType.FULL_BACKUP);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyKeyValueBackup_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.KEYVALUE_BACKUP);
+        testServiceDefaultValue_On(ServiceType.KEYVALUE_BACKUP);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyAnimation_DefaultValueCorrect() {
-        testServiceDefaultValue_unchanged(ServiceType.ANIMATION);
+        testServiceDefaultValue_Off(ServiceType.ANIMATION);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyBatteryStats_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.BATTERY_STATS);
+        testServiceDefaultValue_On(ServiceType.BATTERY_STATS);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyNetworkFirewall_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.NETWORK_FIREWALL);
+        testServiceDefaultValue_On(ServiceType.NETWORK_FIREWALL);
     }
 
     @SmallTest
@@ -160,12 +161,12 @@
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyScreenBrightness_DefaultValueCorrect() {
-        testServiceDefaultValue_unchanged(ServiceType.SCREEN_BRIGHTNESS);
+        testServiceDefaultValue_Off(ServiceType.SCREEN_BRIGHTNESS);
     }
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyGps_DefaultValueCorrect() {
-        testServiceDefaultValue(ServiceType.GPS);
+        testServiceDefaultValue_On(ServiceType.GPS);
 
         PowerSaveState stateOn =
                 mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS, true);
@@ -173,6 +174,11 @@
     }
 
     @SmallTest
+    public void testGetBatterySaverPolicy_PolicyQuickDoze_DefaultValueCorrect() {
+        testServiceDefaultValue_Off(ServiceType.QUICK_DOZE);
+    }
+
+    @SmallTest
     public void testUpdateConstants_getCorrectData() {
         mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, "");
 
@@ -214,6 +220,10 @@
                 mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS, BATTERY_SAVER_ON);
         assertThat(gpsState.batterySaverEnabled).isTrue();
         assertThat(gpsState.gpsMode).isEqualTo(GPS_MODE);
+
+        final PowerSaveState quickDozeState = mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.QUICK_DOZE, BATTERY_SAVER_ON);
+        assertThat(quickDozeState.batterySaverEnabled).isTrue();
     }
 
     @SmallTest
@@ -223,7 +233,7 @@
         mBatterySaverPolicy.updateConstantsLocked(null, "");
     }
 
-    private void testServiceDefaultValue(@ServiceType int type) {
+    private void testServiceDefaultValue_On(@ServiceType int type) {
         mBatterySaverPolicy.updateConstantsLocked("", "");
         final PowerSaveState batterySaverStateOn =
                 mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_ON);
@@ -234,7 +244,7 @@
         assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
     }
 
-    private void testServiceDefaultValue_unchanged(@ServiceType int type) {
+    private void testServiceDefaultValue_Off(@ServiceType int type) {
         mBatterySaverPolicy.updateConstantsLocked("", "");
         final PowerSaveState batterySaverStateOn =
                 mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_ON);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 869f8fa0..caaa0bb 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -43,6 +43,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
@@ -92,6 +93,8 @@
     private static final int USER_ID = 0;
     private static final int USER_ID2 = 10;
 
+    private static final String PACKAGE_UNKNOWN = "com.example.unknown";
+
     private static final String ADMIN_PKG = "com.android.admin";
     private static final String ADMIN_PKG2 = "com.android.admin2";
     private static final String ADMIN_PKG3 = "com.android.admin3";
@@ -106,6 +109,9 @@
     // Short STABLE_CHARGING_THRESHOLD for testing purposes
     private static final long STABLE_CHARGING_THRESHOLD = 2000;
 
+    /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
+    private static boolean isPackageInstalled = true;
+
     private MyInjector mInjector;
     private AppStandbyController mController;
 
@@ -183,6 +189,12 @@
         }
 
         @Override
+        boolean isPackageInstalled(String packageName, int flags, int userId) {
+            // Should always return true (default value) unless testing for an uninstalled app
+            return isPackageInstalled;
+        }
+
+        @Override
         int[] getRunningUserIds() {
             return new int[] {USER_ID};
         }
@@ -403,30 +415,30 @@
                         false));
     }
 
-    private void reportEvent(AppStandbyController controller, int eventType,
-            long elapsedTime) {
+    private void reportEvent(AppStandbyController controller, int eventType, long elapsedTime,
+            String packageName) {
         // Back to ACTIVE on event
         mInjector.mElapsedRealtime = elapsedTime;
         UsageEvents.Event ev = new UsageEvents.Event();
-        ev.mPackage = PACKAGE_1;
+        ev.mPackage = packageName;
         ev.mEventType = eventType;
         controller.reportEvent(ev, elapsedTime, USER_ID);
     }
 
-    private int getStandbyBucket(AppStandbyController controller) {
-        return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+    private int getStandbyBucket(AppStandbyController controller, String packageName) {
+        return controller.getAppStandbyBucket(packageName, USER_ID, mInjector.mElapsedRealtime,
                 true);
     }
 
     private void assertBucket(int bucket) {
-        assertEquals(bucket, getStandbyBucket(mController));
+        assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
     public void testBuckets() throws Exception {
         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
 
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
 
         // ACTIVE bucket
         assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
@@ -443,7 +455,7 @@
         // RARE bucket
         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
 
-        reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1);
+        reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1);
 
         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
 
@@ -452,12 +464,48 @@
     }
 
     @Test
+    public void testSetAppStandbyBucket() throws Exception {
+        // For a known package, standby bucket should be set properly
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_TIMEOUT, HOUR_MS);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+
+        // For an unknown package, standby bucket should not be set, hence NEVER is returned
+        // Ensure the unknown package is not already in history by removing it
+        mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
+        isPackageInstalled = false; // Mock package is not installed
+        mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_TIMEOUT, HOUR_MS);
+        isPackageInstalled = true; // Reset mocked variable for other tests
+        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+    }
+
+    @Test
+    public void testAppStandbyBucketOnInstallAndUninstall() throws Exception {
+        // On package install, standby bucket should be ACTIVE
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_UNKNOWN);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+
+        // On uninstall, package should not exist in history and should return a NEVER bucket
+        mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
+        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
+        // Ensure uninstalled app is not in history
+        List<AppStandbyInfo> buckets = mController.getAppStandbyBuckets(USER_ID);
+        for(AppStandbyInfo bucket : buckets) {
+            if (bucket.mPackageName.equals(PACKAGE_UNKNOWN)) {
+                fail("packageName found in app idle history after uninstall.");
+            }
+        }
+    }
+
+    @Test
     public void testScreenTimeAndBuckets() throws Exception {
         mInjector.setDisplayOn(false);
 
         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
 
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
 
         // ACTIVE bucket
         assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
@@ -468,7 +516,7 @@
         // RARE bucket, should fail because the screen wasn't ON.
         mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
         mController.checkIdleStates(USER_ID);
-        assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+        assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
 
         mInjector.setDisplayOn(true);
         assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
@@ -477,7 +525,7 @@
     @Test
     public void testForcedIdle() throws Exception {
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
-        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
         assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
 
         mController.forceIdleState(PACKAGE_1, USER_ID, false);
@@ -488,35 +536,35 @@
 
     @Test
     public void testNotificationEvent() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
         mInjector.mElapsedRealtime = 1;
-        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
 
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
-        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
     public void testSlicePinnedEvent() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
         mInjector.mElapsedRealtime = 1;
-        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
 
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
-        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+        reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
     public void testSlicePinnedPrivEvent() throws Exception {
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
-        reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
@@ -524,28 +572,28 @@
         // Set it to timeout or usage, so that prediction can override it
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
                 REASON_MAIN_TIMEOUT, HOUR_MS);
-        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
                 REASON_MAIN_PREDICTED, HOUR_MS);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
 
         // Fast forward 12 hours
         mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
         mController.checkIdleStates(USER_ID);
         // Should still be in predicted bucket, since prediction timeout is 1 day since prediction
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
         // Fast forward two more hours
         mInjector.mElapsedRealtime += 2 * HOUR_MS;
         mController.checkIdleStates(USER_ID);
         // Should have now applied prediction timeout
-        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
 
         // Fast forward RARE bucket
         mInjector.mElapsedRealtime += RARE_THRESHOLD;
         mController.checkIdleStates(USER_ID);
         // Should continue to apply prediction timeout
-        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
@@ -553,33 +601,33 @@
         // Can force to NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
                 REASON_MAIN_FORCED, 1 * HOUR_MS);
-        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
 
         // Prediction can't override FORCED reason
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
                 REASON_MAIN_FORCED, 1 * HOUR_MS);
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
                 REASON_MAIN_PREDICTED, 1 * HOUR_MS);
-        assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
 
         // Prediction can't override NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
                 REASON_MAIN_DEFAULT, 2 * HOUR_MS);
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
                 REASON_MAIN_PREDICTED, 2 * HOUR_MS);
-        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
 
         // Prediction can't set to NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
                 REASON_MAIN_USAGE, 2 * HOUR_MS);
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
                 REASON_MAIN_PREDICTED, 2 * HOUR_MS);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
     }
 
     @Test
     public void testTimeout() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         mInjector.mElapsedRealtime = 2000;
@@ -601,10 +649,10 @@
 
     @Test
     public void testCascadingTimeouts() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
-        reportEvent(mController, NOTIFICATION_SEEN, 1000);
+        reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
@@ -622,14 +670,15 @@
 
     @Test
     public void testOverlappingTimeouts() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
-        reportEvent(mController, NOTIFICATION_SEEN, 1000);
+        reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // Overlapping USER_INTERACTION before previous one times out
-        reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000);
+        reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000,
+                PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // Still in ACTIVE after first USER_INTERACTION times out
@@ -654,14 +703,14 @@
     public void testSystemInteractionTimeout() throws Exception {
         setChargingState(mController, false);
 
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         // Fast forward to RARE
         mInjector.mElapsedRealtime = RARE_THRESHOLD + 100;
         mController.checkIdleStates(USER_ID);
         assertBucket(STANDBY_BUCKET_RARE);
 
         // Trigger a SYSTEM_INTERACTION and verify bucket
-        reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
+        reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // Verify it's still in ACTIVE close to end of timeout
@@ -677,11 +726,11 @@
 
     @Test
     public void testPredictionNotOverridden() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000;
-        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // Falls back to WORKING_SET
@@ -703,7 +752,7 @@
 
     @Test
     public void testPredictionStrikesBack() throws Exception {
-        reportEvent(mController, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // Predict to FREQUENT
@@ -714,7 +763,7 @@
 
         // Add a short timeout event
         mInjector.mElapsedRealtime += 1000;
-        reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
+        reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
         mInjector.mElapsedRealtime += 1000;
         mController.checkIdleStates(USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 5a787ec..473682d 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -51,6 +51,10 @@
     private IntervalStats mIntervalStats = new IntervalStats();
     private long mEndTime = 0;
 
+    // Key under which the payload blob is stored
+    // same as UsageStatsBackupHelper.KEY_USAGE_STATS
+    static final String KEY_USAGE_STATS = "usage_stats";
+
     private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier =
             new UsageStatsDatabase.StatCombiner<IntervalStats>() {
                 @Override
@@ -104,10 +108,11 @@
 
     private void populateIntervalStats() {
         final int numberOfEvents = 3000;
-        long time = 1;
+        final int timeProgression = 23;
+        long time = System.currentTimeMillis() - (numberOfEvents*timeProgression);
         mIntervalStats = new IntervalStats();
 
-        mIntervalStats.beginTime = 1;
+        mIntervalStats.beginTime = time;
         mIntervalStats.interactiveTracker.count = 2;
         mIntervalStats.interactiveTracker.duration = 111111;
         mIntervalStats.nonInteractiveTracker.count = 3;
@@ -158,7 +163,7 @@
             mIntervalStats.events.insert(event);
             mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
 
-            time += 23; // Arbitrary progression of time
+            time += timeProgression; // Arbitrary progression of time
         }
         mEndTime = time;
 
@@ -286,9 +291,19 @@
         }
         assertEquals(stats1.activeConfiguration, stats2.activeConfiguration);
 
-        assertEquals(stats1.events.size(), stats2.events.size());
-        for (int i = 0; i < stats1.events.size(); i++) {
-            compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i);
+        if (stats1.events == null) {
+            // If stats1 events are null, stats2 should be null or empty
+            if (stats2.events != null) {
+                assertEquals(stats2.events.size(), 0);
+            }
+        } else if (stats2.events == null) {
+            // If stats2 events are null, stats1 should be null or empty
+            assertEquals(stats1.events.size(), 0);
+        } else {
+            assertEquals(stats1.events.size(), stats2.events.size());
+            for (int i = 0; i < stats1.events.size(); i++) {
+                compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i);
+            }
         }
     }
 
@@ -340,6 +355,47 @@
     }
 
     /**
+     * Runs the Backup and Restore tests.
+     * Will write the generated IntervalStat to a database and create a backup in the specified
+     * version's format. The database will then be restored from the blob and the restored
+     * interval stats will be compared to the generated stats.
+     */
+    void runBackupRestoreTest(int version) throws IOException {
+        UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir);
+        prevDB.init(1);
+        prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
+        // Create a backup with a specific version
+        byte[] blob = prevDB.getBackupPayload(KEY_USAGE_STATS, version);
+
+        clearUsageStatsFiles();
+
+        UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
+        newDB.init(1);
+        // Attempt to restore the usage stats from the backup
+        newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
+        List<IntervalStats> stats = newDB.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, mEndTime,
+                mIntervalStatsVerifier);
+
+
+        if (version > newDB.BACKUP_VERSION || version < 1) {
+            if (stats != null && stats.size() != 0) {
+                fail("UsageStatsDatabase should ne be able to restore from unknown data versions");
+            }
+            return;
+        }
+
+        assertEquals(1, stats.size());
+
+        // Clear non backed up data from expected IntervalStats
+        mIntervalStats.activeConfiguration = null;
+        mIntervalStats.configurations.clear();
+        if (mIntervalStats.events != null) mIntervalStats.events.clear();
+
+        // The written and read IntervalStats should match
+        compareIntervalStats(mIntervalStats, stats.get(0));
+    }
+
+    /**
      * Test the version upgrade from 3 to 4
      */
     @Test
@@ -349,4 +405,18 @@
         runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY);
         runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
     }
+
+
+    /**
+     * Test the version upgrade from 3 to 4
+     */
+    @Test
+    public void testBackupRestore() throws IOException {
+        runBackupRestoreTest(1);
+        runBackupRestoreTest(4);
+
+        // test invalid backup versions as well
+        runBackupRestoreTest(0);
+        runBackupRestoreTest(99999);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 3dcdd23..cb8ca7e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -39,6 +39,7 @@
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doNothing;
@@ -288,34 +289,25 @@
         final WindowTestUtils.TestAppWindowToken token =
                 WindowTestUtils.createTestAppWindowToken(dc0);
         task0.addChild(token, 0);
-        dc0.mTapDetector = new TaskTapPointerEventListener(sWm, dc0);
-        sWm.registerPointerEventListener(dc0.mTapDetector);
+        dc0.configureDisplayPolicy();
+        assertNotNull(dc0.mTapDetector);
+
         final TaskStack stack1 = createTaskStackOnDisplay(dc1);
         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
         final WindowTestUtils.TestAppWindowToken token1 =
                 WindowTestUtils.createTestAppWindowToken(dc0);
         task1.addChild(token1, 0);
-        dc1.mTapDetector = new TaskTapPointerEventListener(sWm, dc0);
-        sWm.registerPointerEventListener(dc1.mTapDetector);
+        dc1.configureDisplayPolicy();
+        assertNotNull(dc1.mTapDetector);
 
-        // tap on primary display (by sending ACTION_DOWN followed by ACTION_UP)
-        DisplayMetrics dm0 = dc0.getDisplayMetrics();
-        dc0.mTapDetector.onPointerEvent(
-                createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, true));
-        dc0.mTapDetector.onPointerEvent(
-                createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false));
-
+        // tap on primary display.
+        tapOnDisplay(dc0);
         // Check focus is on primary display.
         assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
                 dc0.findFocusedWindow());
 
-        // Tap on secondary display
-        DisplayMetrics dm1 = dc1.getDisplayMetrics();
-        dc1.mTapDetector.onPointerEvent(
-                createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, true));
-        dc1.mTapDetector.onPointerEvent(
-                createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false));
-
+        // Tap on secondary display.
+        tapOnDisplay(dc1);
         // Check focus is on secondary.
         assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
                 dc1.findFocusedWindow());
@@ -626,17 +618,32 @@
         return result;
     }
 
-    private MotionEvent createTapEvent(float x, float y, boolean isDownEvent) {
+    private void tapOnDisplay(final DisplayContent dc) {
+        final DisplayMetrics dm = dc.getDisplayMetrics();
+        final float x = dm.widthPixels / 2;
+        final float y = dm.heightPixels / 2;
         final long downTime = SystemClock.uptimeMillis();
         final long eventTime = SystemClock.uptimeMillis() + 100;
-        final int metaState = 0;
-
-        return MotionEvent.obtain(
+        // sending ACTION_DOWN
+        final MotionEvent downEvent = MotionEvent.obtain(
                 downTime,
-                eventTime,
-                isDownEvent ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP,
+                downTime,
+                MotionEvent.ACTION_DOWN,
                 x,
                 y,
-                metaState);
+                0 /*metaState*/);
+        downEvent.setDisplayId(dc.getDisplayId());
+        dc.mTapDetector.onPointerEvent(downEvent);
+
+        // sending ACTION_UP
+        final MotionEvent upEvent = MotionEvent.obtain(
+                downTime,
+                eventTime,
+                MotionEvent.ACTION_UP,
+                x,
+                y,
+                0 /*metaState*/);
+        upEvent.setDisplayId(dc.getDisplayId());
+        dc.mTapDetector.onPointerEvent(upEvent);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
index 07eafa5..a028d5ee 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -18,23 +18,34 @@
 package com.android.server.wm;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.app.WindowConfiguration;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Surface;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.policy.WindowManagerPolicy;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
 
+/**
+ * Tests for the {@link DisplaySettings} class.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:com.android.server.wm.DisplaySettingsTests
+ */
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -155,6 +166,71 @@
         assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
     }
 
+    @Test
+    public void testDefaultToFreeUserRotation() {
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
+        assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, rotation.getUserRotationMode());
+        assertFalse(rotation.isRotationFrozen());
+    }
+
+    @Test
+    public void testDefaultTo0DegRotation() {
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(Surface.ROTATION_0, mSecondaryDisplay.getDisplayRotation().getUserRotation());
+    }
+
+    @Test
+    public void testPersistUserRotationModeInSameInstance() {
+        mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+                Surface.ROTATION_90);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
+        assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode());
+        assertTrue(rotation.isRotationFrozen());
+    }
+
+    @Test
+    public void testPersistUserRotationInSameInstance() {
+        mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+                Surface.ROTATION_90);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(Surface.ROTATION_90, mSecondaryDisplay.getDisplayRotation().getUserRotation());
+    }
+
+    @Test
+    public void testPersistUserRotationModeAcrossInstances() {
+        mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+                Surface.ROTATION_270);
+        mTarget.writeSettingsLocked();
+
+        DisplaySettings target = new DisplaySettings(sWm, mTestFolder);
+        target.readSettingsLocked();
+
+        target.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation();
+        assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode());
+        assertTrue(rotation.isRotationFrozen());
+    }
+
+    @Test
+    public void testPersistUserRotationAcrossInstances() {
+        mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+                Surface.ROTATION_270);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(Surface.ROTATION_270,
+                mSecondaryDisplay.getDisplayRotation().getUserRotation());
+    }
+
     private static void assertOverscan(DisplayContent display, int left, int top, int right,
             int bottom) {
         final DisplayInfo info = display.getDisplayInfo();
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 60025f0..ae40f7e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -334,7 +334,7 @@
         final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
         waitForIdle();
 
-        assertEquals(displayId, activity.getDisplay().getDisplayId());
+        assertEquals(displayId, activity.getDisplayId());
         return activity;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index ced0847..f8e7403 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -30,6 +30,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.InputChannel;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -43,6 +44,7 @@
  * atest com.android.server.wm.TaskPositioningControllerTests
  */
 @SmallTest
+@FlakyTest(bugId = 117924387)
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 public class TaskPositioningControllerTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 474e5b7..5159551 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -432,17 +432,6 @@
     }
 
     @Override
-    public int getUserRotationMode() {
-        return 0;
-    }
-
-    @Override
-    public void setUserRotationMode(int mode,
-            int rotation) {
-
-    }
-
-    @Override
     public int adjustSystemUiVisibilityLw(int visibility) {
         return 0;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
index 54fd7db..389eba5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -34,8 +34,8 @@
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.view.InputChannel;
-
-import androidx.test.InstrumentationRegistry;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 
 import com.android.server.LocalServices;
 import com.android.server.input.InputManagerService;
@@ -46,6 +46,12 @@
 import org.junit.runners.model.Statement;
 import org.mockito.invocation.InvocationOnMock;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+
 /**
  * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
  * to properly tear it down after.
@@ -61,6 +67,12 @@
 
     private WindowManagerService mService;
     private TestWindowManagerPolicy mPolicy;
+    // Record all {@link SurfaceControl.Transaction} created while testing and releases native
+    // resources when test finishes.
+    private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
+    // Record all {@link SurfaceControl} created while testing and releases native resources when
+    // test finishes.
+    private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>();
 
     @Override
     public Statement apply(Statement base, Description description) {
@@ -108,12 +120,25 @@
                 // InputChannel is final and can't be mocked.
                 InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
                 if (input != null && input.length > 1) {
-                    doReturn(input[1]).when(ims).monitorInput(anyString());
+                    doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
                 }
 
                 mService = WindowManagerService.main(context, ims, false,
                         false, mPolicy = new TestWindowManagerPolicy(
                                 WindowManagerServiceRule.this::getWindowManagerService));
+                mService.mTransactionFactory = () -> {
+                    final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+                    mSurfaceTransactions.add(new WeakReference<>(transaction));
+                    return transaction;
+                };
+                mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) {
+                    @Override
+                    public SurfaceControl build() {
+                        final SurfaceControl control = super.build();
+                        mSurfaceControls.add(new WeakReference<>(control));
+                        return control;
+                    }
+                };
 
                 mService.onInitReady();
 
@@ -135,6 +160,8 @@
 
             private void tearDown() {
                 waitUntilWindowManagerHandlersIdle();
+                destroyAllSurfaceTransactions();
+                destroyAllSurfaceControls();
                 removeServices();
                 mService = null;
                 mPolicy = null;
@@ -158,4 +185,24 @@
             SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
         }
     }
+
+    private void destroyAllSurfaceTransactions() {
+        for (final WeakReference<Transaction> reference : mSurfaceTransactions) {
+            final Transaction transaction = reference.get();
+            if (transaction != null) {
+                reference.clear();
+                transaction.close();
+            }
+        }
+    }
+
+    private void destroyAllSurfaceControls() {
+        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
+            final SurfaceControl control = reference.get();
+            if (control != null) {
+                reference.clear();
+                control.destroy();
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index a610e6e..3a8c4ae 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -98,10 +98,13 @@
             super(s);
         }
 
+        @Override
         public SurfaceControl.Builder setParent(SurfaceControl sc) {
             mPendingParent = sc;
             return super.setParent(sc);
         }
+
+        @Override
         public SurfaceControl build() {
             SurfaceControl sc = super.build();
             mParentFor.put(sc, mPendingParent);
@@ -110,7 +113,7 @@
         }
     }
 
-    class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+    private class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
         public SurfaceControl.Builder make(SurfaceSession s) {
             return new HierarchyRecorder(s);
         }
@@ -131,6 +134,7 @@
     @After
     public void after() {
         mTransaction.close();
+        mParentFor.keySet().forEach(SurfaceControl::destroy);
         mParentFor.clear();
     }
 
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
index 7e7decf..ab222b9 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := androidx-test ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.runner ub-uiautomator
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4e007c2d..3266b8b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1689,7 +1689,7 @@
     }
 
     @Test
-    public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
+    public void testGetNotificationChannelFromPrivilegedListener_cdm_success() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
@@ -1703,7 +1703,7 @@
     }
 
     @Test
-    public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
+    public void testGetNotificationChannelFromPrivilegedListener_cdm_noAccess() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1721,6 +1721,38 @@
     }
 
     @Test
+    public void testGetNotificationChannelFromPrivilegedListener_assistant_success()
+            throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
+
+        mBinderService.getNotificationChannelsFromPrivilegedListener(
+                null, PKG, Process.myUserHandle());
+
+        verify(mPreferencesHelper, times(1)).getNotificationChannels(
+                anyString(), anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testGetNotificationChannelFromPrivilegedListener_assistant_noAccess() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(new ArrayList<>());
+        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(false);
+
+        try {
+            mBinderService.getNotificationChannelsFromPrivilegedListener(
+                    null, PKG, Process.myUserHandle());
+            fail("listeners that don't have a companion device shouldn't be able to call this");
+        } catch (SecurityException e) {
+            // pass
+        }
+
+        verify(mPreferencesHelper, never()).getNotificationChannels(
+                anyString(), anyInt(), anyBoolean());
+    }
+
+    @Test
     public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         List<String> associations = new ArrayList<>();
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
index 0f8b18a..c095ae0 100644
--- a/services/tests/wmtests/Android.mk
+++ b/services/tests/wmtests/Android.mk
@@ -14,7 +14,7 @@
     $(call all-java-files-under, ../servicestests/utils)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx-test \
+    androidx.test.runner \
     mockito-target-minus-junit4 \
     platform-test-annotations \
 
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 4e99732..bc54a5d 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -320,14 +320,7 @@
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
                 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
-        if (appUsageHistory == null) {
-            return false; // Default to not idle
-        } else {
-            return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
-            // Whether or not it's passed will now be externally calculated and the
-            // bucket will be pushed to the history using setAppStandbyBucket()
-            //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
-        }
+        return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE;
     }
 
     public AppUsageHistory getAppUsageHistory(String packageName, int userId,
@@ -404,17 +397,19 @@
     public long getTimeSinceLastJobRun(String packageName, int userId, long elapsedRealtime) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
-                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+                getPackageHistory(userHistory, packageName, elapsedRealtime, false);
         // Don't adjust the default, else it'll wrap around to a positive value
-        if (appUsageHistory.lastJobRunTime == Long.MIN_VALUE) return Long.MAX_VALUE;
+        if (appUsageHistory == null || appUsageHistory.lastJobRunTime == Long.MIN_VALUE) {
+            return Long.MAX_VALUE;
+        }
         return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
     }
 
     public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
-                getPackageHistory(userHistory, packageName, elapsedRealtime, true);
-        return appUsageHistory.currentBucket;
+                getPackageHistory(userHistory, packageName, elapsedRealtime, false);
+        return appUsageHistory == null ? STANDBY_BUCKET_NEVER : appUsageHistory.currentBucket;
     }
 
     public ArrayList<AppStandbyInfo> getAppStandbyBuckets(int userId, boolean appIdleEnabled) {
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 02ad3a8..6a74564 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -1161,6 +1161,10 @@
     void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
             int reason, long elapsedRealtime, boolean resetTimeout) {
         synchronized (mAppIdleLock) {
+            // If the package is not installed, don't allow the bucket to be set.
+            if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+                return;
+            }
             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
                     userId, elapsedRealtime);
             boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
@@ -1594,6 +1598,10 @@
             return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
         }
 
+        boolean isPackageInstalled(String packageName, int flags, int userId) {
+            return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
+        }
+
         int[] getRunningUserIds() throws RemoteException {
             return ActivityManager.getService().getRunningUserIds();
         }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 8946d25..0e15947 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -58,31 +58,31 @@
  * When the UsageStatsDatabase version is upgraded, the files on disk are migrated to the new
  * version on init. The steps of migration are as follows:
  * 1) Check if version upgrade breadcrumb exists on disk, if so skip to step 4.
- * 2) Copy current files to versioned backup files.
- * 3) Write a temporary breadcrumb file with some info about the backed up files.
- * 4) Deserialize a versioned backup file using the info written to the breadcrumb for the
- * correct deserialization methodology.
+ * 2) Move current files to a timestamped backup directory.
+ * 3) Write a temporary breadcrumb file with some info about the backup directory.
+ * 4) Deserialize the backup files in the timestamped backup folder referenced by the breadcrumb.
  * 5) Reserialize the data read from the file with the new version format and replace the old files
- * 6) Repeat Step 3 and 4 for each versioned backup file matching the breadcrumb file.
+ * 6) Repeat Step 3 and 4 for each file in the backup folder.
  * 7) Update the version file with the new version and build fingerprint.
- * 8) Delete the versioned backup files (unless flagged to be kept).
+ * 8) Delete the time stamped backup folder (unless flagged to be kept).
  * 9) Delete the breadcrumb file.
  *
  * Performing the upgrade steps in this order, protects against unexpected shutdowns mid upgrade
  *
- * A versioned backup file is simply a copy of a Usage Stats file with some extra info embedded in
- * the file name. The structure of the versioned backup filename is as followed:
- * (original file name).(backup timestamp).(original file version).vak
- *
- * During the version upgrade process, the new upgraded file will have it's name set to the original
- * file name. The backup timestamp helps distinguish between versioned backups if multiple upgrades
- * and downgrades have taken place. The original file version denotes how to parse the file.
+ * The backup directory will contain directories with timestamp names. If the upgrade breadcrumb
+ * exists on disk, it will contain a timestamp which will match one of the backup directories. The
+ * breadcrumb will also contain a version number which will denote how the files in the backup
+ * directory should be deserialized.
  */
 public class UsageStatsDatabase {
-    private static final int DEFAULT_CURRENT_VERSION = 3;
-
-    // Current version of the backup schema
-    static final int BACKUP_VERSION = 1;
+    private static final int DEFAULT_CURRENT_VERSION = 4;
+    /**
+     * Current version of the backup schema
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static final int BACKUP_VERSION = 4;
 
     // Key under which the payload blob is stored
     // same as UsageStatsBackupHelper.KEY_USAGE_STATS
@@ -91,13 +91,12 @@
     // Persist versioned backup files.
     // Should be false, except when testing new versions
     // STOPSHIP: b/111422946 this should be false on launch
-    static final boolean KEEP_VAK_FILES = true;
+    static final boolean KEEP_BACKUP_DIR = true;
 
     private static final String TAG = "UsageStatsDatabase";
     // STOPSHIP: b/111422946 this should be boolean DEBUG = UsageStatsService.DEBUG; on launch
     private static final boolean DEBUG = true;
     private static final String BAK_SUFFIX = ".bak";
-    private static final String VERSIONED_BAK_SUFFIX = ".vak";
     private static final String CHECKED_IN_SUFFIX = UsageStatsXml.CHECKED_IN_SUFFIX;
     private static final String RETENTION_LEN_KEY = "ro.usagestats.chooser.retention";
     private static final int SELECTION_LOG_RETENTION_LEN =
@@ -108,12 +107,13 @@
     private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
     private final UnixCalendar mCal;
     private final File mVersionFile;
+    private final File mBackupsDir;
     // If this file exists on disk, UsageStatsDatabase is in the middle of migrating files to a new
     // version. If this file exists on boot, the upgrade was interrupted and needs to be picked up
     // where it left off.
     private final File mUpdateBreadcrumb;
     // Current version of the database files schema
-    private final int mCurrentVersion;
+    private int mCurrentVersion;
     private boolean mFirstUpdate;
     private boolean mNewUpdate;
 
@@ -133,6 +133,7 @@
         };
         mCurrentVersion = version;
         mVersionFile = new File(dir, "version");
+        mBackupsDir = new File(dir, "backups");
         mUpdateBreadcrumb = new File(dir, "breadcrumb");
         mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
         mCal = new UnixCalendar(0);
@@ -251,7 +252,7 @@
         final FilenameFilter backupFileFilter = new FilenameFilter() {
             @Override
             public boolean accept(File dir, String name) {
-                return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
+                return !name.endsWith(BAK_SUFFIX);
             }
         };
 
@@ -316,24 +317,33 @@
         if (version != mCurrentVersion) {
             Slog.i(TAG, "Upgrading from version " + version + " to " + mCurrentVersion);
             if (!mUpdateBreadcrumb.exists()) {
-                doUpgradeLocked(version);
+                try {
+                    doUpgradeLocked(version);
+                } catch (Exception e) {
+                    Slog.e(TAG,
+                            "Failed to upgrade from version " + version + " to " + mCurrentVersion,
+                            e);
+                    // Fallback to previous version.
+                    mCurrentVersion = version;
+                    return;
+                }
             } else {
                 Slog.i(TAG, "Version upgrade breadcrumb found on disk! Continuing version upgrade");
             }
+        }
 
-            if (mUpdateBreadcrumb.exists()) {
-                int previousVersion;
-                long token;
-                try (BufferedReader reader = new BufferedReader(
-                        new FileReader(mUpdateBreadcrumb))) {
-                    token = Long.parseLong(reader.readLine());
-                    previousVersion = Integer.parseInt(reader.readLine());
-                } catch (NumberFormatException | IOException e) {
-                    Slog.e(TAG, "Failed read version upgrade breadcrumb");
-                    throw new RuntimeException(e);
-                }
-                continueUpgradeLocked(previousVersion, token);
+        if (mUpdateBreadcrumb.exists()) {
+            int previousVersion;
+            long token;
+            try (BufferedReader reader = new BufferedReader(
+                    new FileReader(mUpdateBreadcrumb))) {
+                token = Long.parseLong(reader.readLine());
+                previousVersion = Integer.parseInt(reader.readLine());
+            } catch (NumberFormatException | IOException e) {
+                Slog.e(TAG, "Failed read version upgrade breadcrumb");
+                throw new RuntimeException(e);
             }
+            continueUpgradeLocked(previousVersion, token);
         }
 
         if (version != mCurrentVersion || mNewUpdate) {
@@ -351,11 +361,12 @@
 
         if (mUpdateBreadcrumb.exists()) {
             // Files should be up to date with current version. Clear the version update breadcrumb
-            if (!KEEP_VAK_FILES) {
-                removeVersionedBackupFiles();
-            }
             mUpdateBreadcrumb.delete();
         }
+
+        if (mBackupsDir.exists() && !KEEP_BACKUP_DIR) {
+            deleteDirectory(mBackupsDir);
+        }
     }
 
     private String getBuildFingerprint() {
@@ -378,22 +389,36 @@
                 }
             }
         } else {
-            // Turn all current usage stats files into versioned backup files
+            // Create a dir in backups based on current timestamp
             final long token = System.currentTimeMillis();
-            final FilenameFilter backupFileFilter = new FilenameFilter() {
-                @Override
-                public boolean accept(File dir, String name) {
-                    return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
-                }
-            };
+            final File backupDir = new File(mBackupsDir, Long.toString(token));
+            backupDir.mkdirs();
+            if (!backupDir.exists()) {
+                throw new IllegalStateException(
+                        "Failed to create backup directory " + backupDir.getAbsolutePath());
+            }
+            try {
+                Files.copy(mVersionFile.toPath(),
+                        new File(backupDir, mVersionFile.getName()).toPath(),
+                        StandardCopyOption.REPLACE_EXISTING);
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to back up version file : " + mVersionFile.toString());
+                throw new RuntimeException(e);
+            }
 
             for (int i = 0; i < mIntervalDirs.length; i++) {
-                File[] files = mIntervalDirs[i].listFiles(backupFileFilter);
+                final File backupIntervalDir = new File(backupDir, mIntervalDirs[i].getName());
+                backupIntervalDir.mkdir();
+
+                if (!backupIntervalDir.exists()) {
+                    throw new IllegalStateException(
+                            "Failed to create interval backup directory "
+                                    + backupIntervalDir.getAbsolutePath());
+                }
+                File[] files = mIntervalDirs[i].listFiles();
                 if (files != null) {
                     for (int j = 0; j < files.length; j++) {
-                        final File backupFile = new File(
-                                files[j].toString() + "." + Long.toString(token) + "."
-                                        + Integer.toString(thisVersion) + VERSIONED_BAK_SUFFIX);
+                        final File backupFile = new File(backupIntervalDir, files[j].getName());
                         if (DEBUG) {
                             Slog.d(TAG, "Creating versioned (" + Integer.toString(thisVersion)
                                     + ") backup of " + files[j].toString()
@@ -403,9 +428,8 @@
 
                         try {
                             // Backup file should not already exist, but make sure it doesn't
-                            Files.deleteIfExists(backupFile.toPath());
                             Files.move(files[j].toPath(), backupFile.toPath(),
-                                    StandardCopyOption.ATOMIC_MOVE);
+                                    StandardCopyOption.REPLACE_EXISTING);
                         } catch (IOException e) {
                             Slog.e(TAG, "Failed to back up file : " + files[j].toString());
                             throw new RuntimeException(e);
@@ -414,8 +438,7 @@
                 }
             }
 
-            // Leave a breadcrumb behind noting that all the usage stats have been copied to a
-            // versioned backup.
+            // Leave a breadcrumb behind noting that all the usage stats have been moved to a backup
             BufferedWriter writer = null;
             try {
                 writer = new BufferedWriter(new FileWriter(mUpdateBreadcrumb));
@@ -434,18 +457,13 @@
     }
 
     private void continueUpgradeLocked(int version, long token) {
-        // Read all the backed ups for the specified version and rewrite them with the current
-        // version's file format.
-        final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                return name.endsWith("." + Long.toString(token) + "." + Integer.toString(version)
-                        + VERSIONED_BAK_SUFFIX);
-            }
-        };
+        final File backupDir = new File(mBackupsDir, Long.toString(token));
 
+        // Read each file in the backup according to the version and write to the interval
+        // directories in the current versions format
         for (int i = 0; i < mIntervalDirs.length; i++) {
-            File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+            final File backedUpInterval = new File(backupDir, mIntervalDirs[i].getName());
+            File[] files = backedUpInterval.listFiles();
             if (files != null) {
                 for (int j = 0; j < files.length; j++) {
                     if (DEBUG) {
@@ -459,34 +477,9 @@
                         readLocked(new AtomicFile(files[j]), stats, version);
                         writeLocked(new AtomicFile(new File(mIntervalDirs[i],
                                 Long.toString(stats.beginTime))), stats, mCurrentVersion);
-                    } catch (IOException e) {
-                        Slog.e(TAG,
-                                "Failed to upgrade versioned backup file : " + files[j].toString());
-                        throw new RuntimeException(e);
-                    }
-                }
-            }
-        }
-    }
-
-    private void removeVersionedBackupFiles() {
-        final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                return name.endsWith(VERSIONED_BAK_SUFFIX);
-            }
-        };
-
-        for (int i = 0; i < mIntervalDirs.length; i++) {
-            File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
-            if (files != null) {
-                for (int j = 0; j < files.length; j++) {
-                    if (DEBUG) {
-                        Slog.d(TAG,
-                                "Removing " + files[j].toString() + " for interval " + i);
-                    }
-                    if (!files[j].delete()) {
-                        Slog.e(TAG, "Failed to delete file : " + files[j].toString());
+                    } catch (Exception e) {
+                        // This method is called on boot, log the exception and move on
+                        Slog.e(TAG, "Failed to upgrade backup file : " + files[j].toString());
                     }
                 }
             }
@@ -761,7 +754,7 @@
                             }
                         }
                         writeLocked(af, stats);
-                    } catch (IOException e) {
+                    } catch (Exception e) {
                         Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
                     }
                 }
@@ -961,7 +954,7 @@
             sb.append("\nError found in:\n");
             sb.append(file.getBaseFile().getAbsolutePath());
             sb.append("\nPlease go to b/115429334 to help root cause this issue");
-            Slog.wtf(TAG,sb.toString());
+            Slog.wtf(TAG, sb.toString());
         }
     }
 
@@ -1013,40 +1006,53 @@
 
     /* Backup/Restore Code */
     byte[] getBackupPayload(String key) {
+        return getBackupPayload(key, BACKUP_VERSION);
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public byte[] getBackupPayload(String key, int version) {
         synchronized (mLock) {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             if (KEY_USAGE_STATS.equals(key)) {
                 prune(System.currentTimeMillis());
                 DataOutputStream out = new DataOutputStream(baos);
                 try {
-                    out.writeInt(BACKUP_VERSION);
+                    out.writeInt(version);
 
                     out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size());
+
                     for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size();
                             i++) {
                         writeIntervalStatsToStream(out,
-                                mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].valueAt(i));
+                                mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].valueAt(i),
+                                version);
                     }
 
                     out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size());
                     for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size();
                             i++) {
                         writeIntervalStatsToStream(out,
-                                mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].valueAt(i));
+                                mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].valueAt(i),
+                                version);
                     }
 
                     out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size());
                     for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size();
                             i++) {
                         writeIntervalStatsToStream(out,
-                                mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].valueAt(i));
+                                mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].valueAt(i),
+                                version);
                     }
 
                     out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size());
                     for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size();
                             i++) {
                         writeIntervalStatsToStream(out,
-                                mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].valueAt(i));
+                                mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].valueAt(i),
+                                version);
                     }
                     if (DEBUG) Slog.i(TAG, "Written " + baos.size() + " bytes of data");
                 } catch (IOException ioe) {
@@ -1059,7 +1065,11 @@
 
     }
 
-    void applyRestoredPayload(String key, byte[] payload) {
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public void applyRestoredPayload(String key, byte[] payload) {
         synchronized (mLock) {
             if (KEY_USAGE_STATS.equals(key)) {
                 // Read stats files for the current device configs
@@ -1087,28 +1097,32 @@
 
                     int fileCount = in.readInt();
                     for (int i = 0; i < fileCount; i++) {
-                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
+                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
+                                backupDataVersion);
                         stats = mergeStats(stats, dailyConfigSource);
                         putUsageStats(UsageStatsManager.INTERVAL_DAILY, stats);
                     }
 
                     fileCount = in.readInt();
                     for (int i = 0; i < fileCount; i++) {
-                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
+                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
+                                backupDataVersion);
                         stats = mergeStats(stats, weeklyConfigSource);
                         putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, stats);
                     }
 
                     fileCount = in.readInt();
                     for (int i = 0; i < fileCount; i++) {
-                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
+                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
+                                backupDataVersion);
                         stats = mergeStats(stats, monthlyConfigSource);
                         putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, stats);
                     }
 
                     fileCount = in.readInt();
                     for (int i = 0; i < fileCount; i++) {
-                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
+                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
+                                backupDataVersion);
                         stats = mergeStats(stats, yearlyConfigSource);
                         putUsageStats(UsageStatsManager.INTERVAL_YEARLY, stats);
                     }
@@ -1135,7 +1149,7 @@
         return beingRestored;
     }
 
-    private void writeIntervalStatsToStream(DataOutputStream out, AtomicFile statsFile)
+    private void writeIntervalStatsToStream(DataOutputStream out, AtomicFile statsFile, int version)
             throws IOException {
         IntervalStats stats = new IntervalStats();
         try {
@@ -1146,7 +1160,7 @@
             return;
         }
         sanitizeIntervalStatsForBackup(stats);
-        byte[] data = serializeIntervalStats(stats);
+        byte[] data = serializeIntervalStats(stats, version);
         out.writeInt(data.length);
         out.write(data);
     }
@@ -1165,26 +1179,26 @@
         if (stats.events != null) stats.events.clear();
     }
 
-    private byte[] serializeIntervalStats(IntervalStats stats) {
+    private byte[] serializeIntervalStats(IntervalStats stats, int version) {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream out = new DataOutputStream(baos);
         try {
             out.writeLong(stats.beginTime);
-            writeLocked(out, stats);
-        } catch (IOException ioe) {
+            writeLocked(out, stats, version);
+        } catch (Exception ioe) {
             Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
             baos.reset();
         }
         return baos.toByteArray();
     }
 
-    private IntervalStats deserializeIntervalStats(byte[] data) {
+    private IntervalStats deserializeIntervalStats(byte[] data, int version) {
         ByteArrayInputStream bais = new ByteArrayInputStream(data);
         DataInputStream in = new DataInputStream(bais);
         IntervalStats stats = new IntervalStats();
         try {
             stats.beginTime = in.readLong();
-            readLocked(in, stats);
+            readLocked(in, stats, version);
         } catch (IOException ioe) {
             Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
             stats = null;
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 5bf94af..32fc796 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -75,14 +75,21 @@
         if (uid != Process.SYSTEM_UID) {
             enforcePackageBelongsToUid(uid, packageName);
 
-            PackageInfo pkg;
+            int packageTargetSdkVersion;
+            long token = Binder.clearCallingIdentity();
             try {
-                pkg = mContext.getPackageManager().getPackageInfo(packageName, 0);
-            } catch (PackageManager.NameNotFoundException e) {
-                throw new RemoteException("package " + packageName + " cannot be found");
+                PackageInfo pkg;
+                try {
+                    pkg = mContext.getPackageManager().getPackageInfo(packageName, 0);
+                } catch (PackageManager.NameNotFoundException e) {
+                    throw new RemoteException("package " + packageName + " cannot be found");
+                }
+                packageTargetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
 
-            if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+            if (packageTargetSdkVersion >= Build.VERSION_CODES.Q) {
                 if (mContext.checkPermission(android.Manifest.permission.MANAGE_USB, pid, uid)
                         == PackageManager.PERMISSION_DENIED) {
                     UsbUserSettingsManager settings = mSettingsManager.getSettingsForUser(
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index bafb0a2..ad2501d 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -566,6 +566,40 @@
         }
     }
 
+    SoundTrigger.RecognitionEvent getGenericModelState(UUID modelId) {
+        synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_get_generic_model_state", 1);
+            if (modelId == null || mModule == null) {
+                return null;
+            }
+            ModelData modelData = mModelDataMap.get(modelId);
+            if (modelData == null || !modelData.isGenericModel()) {
+                Slog.w(TAG, "GetGenericModelState error: Invalid generic model id:" +
+                        modelId);
+                return null;
+            }
+            if (!modelData.isModelLoaded()) {
+                Slog.i(TAG, "GetGenericModelState: Given generic model is not loaded:" + modelId);
+                return null;
+            }
+            if (!modelData.isModelStarted()) {
+                Slog.i(TAG, "GetGenericModelState: Given generic model is not started:" + modelId);
+                return null;
+            }
+
+            SoundTrigger.RecognitionEvent ret = mModule.getModelState(modelData.getHandle());
+            if (ret == null) {
+                Slog.w(TAG, "GetGenericModelState() call failed");
+            }
+            return ret;
+        }
+    }
+
+    SoundTrigger.RecognitionEvent getKeyphraseModelState(UUID modelId) {
+        Slog.w(TAG, "GetKeyphraseModelState error: Not implemented");
+        return null;
+    }
+
     //---- SoundTrigger.StatusListener methods
     @Override
     public void onRecognition(RecognitionEvent event) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 7c22613..d57fcb1 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -434,6 +434,40 @@
             }
             return mSoundTriggerHelper.isRecognitionRequested(parcelUuid.getUuid());
         }
+
+        @Override
+        public SoundTrigger.RecognitionEvent getModelState(ParcelUuid soundModelId) {
+            enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
+            if (!isInitialized()) return null;
+            if (DEBUG) {
+                Slog.i(TAG, "getModelState(): id = " + soundModelId);
+            }
+
+            synchronized (mLock) {
+                SoundModel soundModel = mLoadedModels.get(soundModelId.getUuid());
+                if (soundModel == null) {
+                    Slog.e(TAG, soundModelId + " is not loaded");
+                    return null;
+                }
+                SoundTrigger.RecognitionEvent ret = null;
+                switch (soundModel.type) {
+                    case SoundModel.TYPE_KEYPHRASE:
+                        ret = mSoundTriggerHelper.getKeyphraseModelState(soundModel.uuid);
+                        break;
+                    case SoundModel.TYPE_GENERIC_SOUND:
+                        ret = mSoundTriggerHelper.getGenericModelState(soundModel.uuid);
+                        break;
+                    default:
+                        Slog.e(TAG, "Unknown model type");
+                        break;
+                }
+                if (ret == null) {
+                    Slog.e(TAG, "Failed to get model state");
+                }
+
+                return ret;
+            }
+        }
     }
 
     /**
diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/TEST_MAPPING
new file mode 100644
index 0000000..8c9d4df
--- /dev/null
+++ b/startop/iorap/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "libiorap-java-tests"
+    }
+  ],
+  "imports": [
+    {
+      "path": "system/iorap"
+    }
+  ]
+}
diff --git a/startop/iorap/tests/AndroidTest.xml b/startop/iorap/tests/AndroidTest.xml
new file mode 100644
index 0000000..f83a16e
--- /dev/null
+++ b/startop/iorap/tests/AndroidTest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<configuration description="Runs libiorap-java-tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="libiorap-java-tests.apk" />
+    </target_preparer>
+
+    <!--
+      Our IIorapIntegrationTest.kt requires setlinux to be disabled:
+      it connects to the iorapd binder service but this requires selinux permissions:
+
+      avc:  denied  { find } for service=iorapd pid=2738 uid=10050
+        scontext=u:r:platform_app:s0:c512,c768 tcontext=u:object_r:iorapd_service:s0
+        tclass=service_manager permissive=0
+    -->
+    <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer">
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.google.android.startop.iorap.tests" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
+
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
index 4ba44a9..16dcbe2 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -77,17 +77,21 @@
             inOrder.verifyNoMoreInteractions()
 
         } finally {
-            iorapService.setTaskListener(null)
+            // iorapService.setTaskListener(null)
+            // FIXME: null is broken, C++ side sees a non-null object.
         }
     }
 
     @Test
     fun testOnPackageEvent() {
+        /*
         testAnyMethod { requestId : RequestId ->
             iorapService.onPackageEvent(requestId,
                     PackageEvent.createReplaced(
                             Uri.parse("https://www.google.com"), "com.fake.package"))
         }
+        */
+        // FIXME: Broken for some reason. C++ side never sees this call.
     }
 
     @Test
diff --git a/startop/tools/view_compiler/Android.bp b/startop/tools/view_compiler/Android.bp
index c3e9184..3681529 100644
--- a/startop/tools/view_compiler/Android.bp
+++ b/startop/tools/view_compiler/Android.bp
@@ -14,19 +14,30 @@
 // limitations under the License.
 //
 
+cc_defaults {
+    name: "viewcompiler_defaults",
+    shared_libs: [
+        "libdexfile",
+        "slicer",
+    ],
+}
+
 cc_library_host_static {
     name: "libviewcompiler",
+    defaults: ["viewcompiler_defaults"],
     srcs: [
+        "dex_builder.cc",
         "java_lang_builder.cc",
         "util.cc",
     ],
     static_libs: [
-        "libbase"
-    ]
+        "libbase",
+    ],
 }
 
 cc_binary_host {
     name: "viewcompiler",
+    defaults: ["viewcompiler_defaults"],
     srcs: [
         "main.cc",
     ],
@@ -40,10 +51,12 @@
 
 cc_test_host {
     name: "view-compiler-tests",
+    defaults: ["viewcompiler_defaults"],
     srcs: [
+        "dex_builder_test.cc",
         "util_test.cc",
     ],
     static_libs: [
         "libviewcompiler",
-    ]
+    ],
 }
diff --git a/startop/tools/view_compiler/dex_builder.cc b/startop/tools/view_compiler/dex_builder.cc
new file mode 100644
index 0000000..7a9f41f
--- /dev/null
+++ b/startop/tools/view_compiler/dex_builder.cc
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_builder.h"
+
+#include "dex/descriptors_names.h"
+#include "dex/dex_instruction.h"
+
+#include <fstream>
+#include <memory>
+
+namespace startop {
+namespace dex {
+
+using std::shared_ptr;
+using std::string;
+
+using art::Instruction;
+using ::dex::kAccPublic;
+
+const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; };
+const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; };
+
+namespace {
+// From https://source.android.com/devices/tech/dalvik/dex-format#dex-file-magic
+constexpr uint8_t kDexFileMagic[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00};
+
+// Strings lengths can be 32 bits long, but encoded as LEB128 this can take up to five bytes.
+constexpr size_t kMaxEncodedStringLength{5};
+
+}  // namespace
+
+void* TrackingAllocator::Allocate(size_t size) {
+  std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(size);
+  void* raw_buffer = buffer.get();
+  allocations_[raw_buffer] = std::move(buffer);
+  return raw_buffer;
+}
+
+void TrackingAllocator::Free(void* ptr) { allocations_.erase(allocations_.find(ptr)); }
+
+// Write out a DEX file that is basically:
+//
+// package dextest;
+// public class DexTest {
+//     public static int foo() { return 5; }
+// }
+void WriteTestDexFile(const string& filename) {
+  DexBuilder dex_file;
+
+  ClassBuilder cbuilder{dex_file.MakeClass("dextest.DexTest")};
+  cbuilder.set_source_file("dextest.java");
+
+  MethodBuilder method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
+
+  MethodBuilder::Register r = method.MakeRegister();
+  method.BuildConst4(r, 5);
+  method.BuildReturn(r);
+
+  method.Encode();
+
+  slicer::MemView image{dex_file.CreateImage()};
+
+  std::ofstream out_file(filename);
+  out_file.write(image.ptr<const char>(), image.size());
+}
+
+DexBuilder::DexBuilder() : dex_file_{std::make_shared<ir::DexFile>()} {
+  dex_file_->magic = slicer::MemView{kDexFileMagic, sizeof(kDexFileMagic)};
+}
+
+slicer::MemView DexBuilder::CreateImage() {
+  ::dex::Writer writer(dex_file_);
+  size_t image_size{0};
+  ::dex::u1* image = writer.CreateImage(&allocator_, &image_size);
+  return slicer::MemView{image, image_size};
+}
+
+ir::String* DexBuilder::GetOrAddString(const std::string& string) {
+  ir::String*& entry = strings_[string];
+
+  if (entry == nullptr) {
+    // Need to encode the length and then write out the bytes, including 1 byte for null terminator
+    auto buffer = std::make_unique<uint8_t[]>(string.size() + kMaxEncodedStringLength + 1);
+    uint8_t* string_data_start = ::dex::WriteULeb128(buffer.get(), string.size());
+
+    size_t header_length =
+        reinterpret_cast<uintptr_t>(string_data_start) - reinterpret_cast<uintptr_t>(buffer.get());
+
+    auto end = std::copy(string.begin(), string.end(), string_data_start);
+    *end = '\0';
+
+    entry = Alloc<ir::String>();
+    // +1 for null terminator
+    entry->data = slicer::MemView{buffer.get(), header_length + string.size() + 1};
+    string_data_.push_back(std::move(buffer));
+  }
+  return entry;
+}
+
+ClassBuilder DexBuilder::MakeClass(const std::string& name) {
+  auto* class_def = Alloc<ir::Class>();
+  ir::Type* type_def = GetOrAddType(art::DotToDescriptor(name.c_str()));
+  type_def->class_def = class_def;
+
+  class_def->type = type_def;
+  class_def->super_class = GetOrAddType(art::DotToDescriptor("java.lang.Object"));
+  class_def->access_flags = kAccPublic;
+  return ClassBuilder{this, class_def};
+}
+
+// TODO(eholk): we probably want GetOrAddString() also
+ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) {
+  if (types_by_descriptor_.find(descriptor) != types_by_descriptor_.end()) {
+    return types_by_descriptor_[descriptor];
+  }
+
+  ir::Type* type = Alloc<ir::Type>();
+  type->descriptor = GetOrAddString(descriptor);
+  types_by_descriptor_[descriptor] = type;
+  return type;
+}
+
+ir::Proto* Prototype::Encode(DexBuilder* dex) const {
+  auto* proto = dex->Alloc<ir::Proto>();
+  proto->shorty = dex->GetOrAddString(Shorty());
+  proto->return_type = dex->GetOrAddType(return_type_.descriptor());
+  if (param_types_.size() > 0) {
+    proto->param_types = dex->Alloc<ir::TypeList>();
+    for (const auto& param_type : param_types_) {
+      proto->param_types->types.push_back(dex->GetOrAddType(param_type.descriptor()));
+    }
+  } else {
+    proto->param_types = nullptr;
+  }
+  return proto;
+}
+
+std::string Prototype::Shorty() const {
+  std::string shorty;
+  shorty.append(return_type_.short_descriptor());
+  for (const auto& type_descriptor : param_types_) {
+    shorty.append(type_descriptor.short_descriptor());
+  }
+  return shorty;
+}
+
+ClassBuilder::ClassBuilder(DexBuilder* parent, ir::Class* class_def)
+    : parent_(parent), class_(class_def) {}
+
+MethodBuilder ClassBuilder::CreateMethod(const std::string& name, Prototype prototype) {
+  ir::String* dex_name{parent_->GetOrAddString(name)};
+
+  auto* decl = parent_->Alloc<ir::MethodDecl>();
+  decl->name = dex_name;
+  decl->parent = class_->type;
+  decl->prototype = prototype.Encode(parent_);
+
+  return MethodBuilder{parent_, class_, decl};
+}
+
+void ClassBuilder::set_source_file(const string& source) {
+  class_->source_file = parent_->GetOrAddString(source);
+}
+
+MethodBuilder::MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl)
+    : dex_{dex}, class_{class_def}, decl_{decl} {}
+
+ir::EncodedMethod* MethodBuilder::Encode() {
+  auto* method = dex_->Alloc<ir::EncodedMethod>();
+  method->decl = decl_;
+
+  // TODO: make access flags configurable
+  method->access_flags = kAccPublic | ::dex::kAccStatic;
+
+  auto* code = dex_->Alloc<ir::Code>();
+  code->registers = num_registers_;
+  // TODO: support ins and outs
+  code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
+  method->code = code;
+
+  class_->direct_methods.push_back(method);
+
+  return method;
+}
+
+MethodBuilder::Register MethodBuilder::MakeRegister() { return num_registers_++; }
+
+void MethodBuilder::BuildReturn() { buffer_.push_back(Instruction::RETURN_VOID); }
+
+void MethodBuilder::BuildReturn(Register src) { buffer_.push_back(Instruction::RETURN | src << 8); }
+
+void MethodBuilder::BuildConst4(Register target, int value) {
+  DCHECK_LT(value, 16);
+  // TODO: support more registers
+  DCHECK_LT(target, 16);
+  buffer_.push_back(Instruction::CONST_4 | (value << 12) | (target << 8));
+}
+
+}  // namespace dex
+}  // namespace startop
diff --git a/startop/tools/view_compiler/dex_builder.h b/startop/tools/view_compiler/dex_builder.h
new file mode 100644
index 0000000..d280abc
--- /dev/null
+++ b/startop/tools/view_compiler/dex_builder.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef DEX_BUILDER_H_
+#define DEX_BUILDER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "slicer/dex_ir.h"
+#include "slicer/writer.h"
+
+namespace startop {
+namespace dex {
+
+// TODO: remove this once the dex generation code is complete.
+void WriteTestDexFile(const std::string& filename);
+
+//////////////////////////
+// Forward declarations //
+//////////////////////////
+class DexBuilder;
+
+// Our custom allocator for dex::Writer
+//
+// This keeps track of all allocations and ensures they are freed when
+// TrackingAllocator is destroyed. Pointers to memory allocated by this
+// allocator must not outlive the allocator.
+class TrackingAllocator : public ::dex::Writer::Allocator {
+ public:
+  virtual void* Allocate(size_t size);
+  virtual void Free(void* ptr);
+
+ private:
+  std::map<void*, std::unique_ptr<uint8_t[]>> allocations_;
+};
+
+// Represents a DEX type descriptor.
+//
+// TODO: add a way to create a descriptor for a reference of a class type.
+class TypeDescriptor {
+ public:
+  // Named constructors for base type descriptors.
+  static const TypeDescriptor Int();
+  static const TypeDescriptor Void();
+
+  // Return the full descriptor, such as I or Ljava/lang/Object
+  const std::string& descriptor() const { return descriptor_; }
+  // Return the shorty descriptor, such as I or L
+  std::string short_descriptor() const { return descriptor().substr(0, 1); }
+
+ private:
+  TypeDescriptor(std::string descriptor) : descriptor_{descriptor} {}
+
+  const std::string descriptor_;
+};
+
+// Defines a function signature. For example, Prototype{TypeDescriptor::VOID, TypeDescriptor::Int}
+// represents the function type (Int) -> Void.
+class Prototype {
+ public:
+  template <typename... TypeDescriptors>
+  Prototype(TypeDescriptor return_type, TypeDescriptors... param_types)
+      : return_type_{return_type}, param_types_{param_types...} {}
+
+  // Encode this prototype into the dex file.
+  ir::Proto* Encode(DexBuilder* dex) const;
+
+  // Get the shorty descriptor, such as VII for (Int, Int) -> Void
+  std::string Shorty() const;
+
+ private:
+  const TypeDescriptor return_type_;
+  const std::vector<TypeDescriptor> param_types_;
+};
+
+// Tools to help build methods and their bodies.
+class MethodBuilder {
+ public:
+  MethodBuilder(DexBuilder* dex, ir::Class* class_def, ir::MethodDecl* decl);
+
+  // Encode the method into DEX format.
+  ir::EncodedMethod* Encode();
+
+  // Registers are just represented by their number.
+  using Register = size_t;
+
+  // Create a new register to be used to storing values. Note that these are not SSA registers, like
+  // might be expected in similar code generators. This does no liveness tracking or anything, so
+  // it's up to the caller to reuse registers as appropriate.
+  Register MakeRegister();
+
+  /////////////////////////////////
+  // Instruction builder methods //
+  /////////////////////////////////
+
+  // return-void
+  void BuildReturn();
+  void BuildReturn(Register src);
+  // const/4
+  void BuildConst4(Register target, int value);
+
+  // TODO: add builders for more instructions
+
+ private:
+  DexBuilder* dex_;
+  ir::Class* class_;
+  ir::MethodDecl* decl_;
+
+  // A buffer to hold instructions we are generating.
+  std::vector<::dex::u2> buffer_;
+
+  // How many registers we've allocated
+  size_t num_registers_;
+};
+
+// A helper to build class definitions.
+class ClassBuilder {
+ public:
+  ClassBuilder(DexBuilder* parent, ir::Class* class_def);
+
+  void set_source_file(const std::string& source);
+
+  // Create a method with the given name and prototype. The returned MethodBuilder can be used to
+  // fill in the method body.
+  MethodBuilder CreateMethod(const std::string& name, Prototype prototype);
+
+ private:
+  DexBuilder* parent_;
+  ir::Class* class_;
+};
+
+// Builds Dex files from scratch.
+class DexBuilder {
+ public:
+  DexBuilder();
+
+  // Create an in-memory image of the DEX file that can either be loaded directly or written to a
+  // file.
+  slicer::MemView CreateImage();
+
+  template <typename T>
+  T* Alloc() {
+    return dex_file_->Alloc<T>();
+  }
+
+  // Find the ir::String that matches the given string, creating it if it does not exist.
+  ir::String* GetOrAddString(const std::string& string);
+  // Create a new class of the given name.
+  ClassBuilder MakeClass(const std::string& name);
+
+  // Add a type for the given descriptor, or return the existing one if it already exists.
+  // See the TypeDescriptor class for help generating these.
+  ir::Type* GetOrAddType(const std::string& descriptor);
+
+ private:
+  std::shared_ptr<ir::DexFile> dex_file_;
+
+  // allocator_ is needed to be able to encode the image.
+  TrackingAllocator allocator_;
+
+  // We'll need to allocate buffers for all of the encoded strings we create. This is where we store
+  // all of them.
+  std::vector<std::unique_ptr<uint8_t[]>> string_data_;
+
+  // Keep track of what types we've defined so we can look them up later.
+  std::map<std::string, ir::Type*> types_by_descriptor_;
+
+  // Keep track of what strings we've defined so we can look them up later.
+  std::map<std::string, ir::String*> strings_;
+};
+
+}  // namespace dex
+}  // namespace startop
+
+#endif  // DEX_BUILDER_H_
diff --git a/startop/tools/view_compiler/dex_builder_test.cc b/startop/tools/view_compiler/dex_builder_test.cc
new file mode 100644
index 0000000..0d8b854
--- /dev/null
+++ b/startop/tools/view_compiler/dex_builder_test.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_builder.h"
+
+#include "dex/art_dex_file_loader.h"
+#include "dex/dex_file.h"
+#include "gtest/gtest.h"
+
+using namespace startop::dex;
+
+// Takes a DexBuilder, encodes it into an in-memory DEX file, verifies the resulting DEX file and
+// returns whether the verification was successful.
+bool EncodeAndVerify(DexBuilder* dex_file) {
+  slicer::MemView image{dex_file->CreateImage()};
+
+  art::ArtDexFileLoader loader;
+  std::string error_msg;
+  std::unique_ptr<const art::DexFile> loaded_dex_file{loader.Open(image.ptr<const uint8_t>(),
+                                                                  image.size(),
+                                                                  /*location=*/"",
+                                                                  /*location_checksum=*/0,
+                                                                  /*oat_dex_file=*/nullptr,
+                                                                  /*verify=*/true,
+                                                                  /*verify_checksum=*/false,
+                                                                  &error_msg)};
+  return loaded_dex_file != nullptr;
+}
+
+TEST(DexBuilderTest, VerifyDexWithClassMethod) {
+  DexBuilder dex_file;
+
+  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+  auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void()})};
+  method.BuildReturn();
+  method.Encode();
+
+  EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
+
+// Makes sure a bad DEX class fails to verify.
+TEST(DexBuilderTest, VerifyBadDexWithClassMethod) {
+  DexBuilder dex_file;
+
+  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+  // This method has the error, because methods cannot take Void() as a parameter.
+  auto method{
+      cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Void(), TypeDescriptor::Void()})};
+  method.BuildReturn();
+  method.Encode();
+
+  EXPECT_FALSE(EncodeAndVerify(&dex_file));
+}
+
+TEST(DexBuilderTest, VerifyDexReturn5) {
+  DexBuilder dex_file;
+
+  auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+  auto method{cbuilder.CreateMethod("foo", Prototype{TypeDescriptor::Int()})};
+  auto r = method.MakeRegister();
+  method.BuildConst4(r, 5);
+  method.BuildReturn(r);
+  method.Encode();
+
+  EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
diff --git a/startop/tools/view_compiler/main.cc b/startop/tools/view_compiler/main.cc
index 0ad7e24..7d791c2 100644
--- a/startop/tools/view_compiler/main.cc
+++ b/startop/tools/view_compiler/main.cc
@@ -16,6 +16,7 @@
 
 #include "gflags/gflags.h"
 
+#include "dex_builder.h"
 #include "java_lang_builder.h"
 #include "util.h"
 
@@ -27,15 +28,17 @@
 #include <string>
 #include <vector>
 
+namespace {
+
 using namespace tinyxml2;
 using std::string;
 
 constexpr char kStdoutFilename[]{"stdout"};
 
-DEFINE_string(package, "", "The package name for the generated class (required)");
+DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
 DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
+DEFINE_string(package, "", "The package name for the generated class (required)");
 
-namespace {
 class ViewCompilerXmlVisitor : public XMLVisitor {
  public:
   ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
@@ -63,6 +66,7 @@
  private:
   JavaLangViewBuilder* builder_;
 };
+
 }  // end namespace
 
 int main(int argc, char** argv) {
@@ -82,6 +86,11 @@
     return 1;
   }
 
+  if (FLAGS_dex) {
+    startop::dex::WriteTestDexFile("test.dex");
+    return 0;
+  }
+
   const char* const filename = argv[kFileNameParam];
   const string layout_name = FindLayoutNameFromFilename(filename);
 
@@ -102,4 +111,4 @@
   xml.Accept(&visitor);
 
   return 0;
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 8454d12..d9e7167 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.job.JobService;
@@ -2889,100 +2890,131 @@
         public static final String SUBSCRIPTION_ID = "sub_id";
 
         /**
-         * The profile_id to which the APN saved in modem
+         * The profile_id to which the APN saved in modem.
          * <p>Type: INTEGER</p>
          *@hide
          */
         public static final String PROFILE_ID = "profile_id";
 
         /**
-         * Is the apn setting to be set in modem
-         * <P>Type: INTEGER (boolean)</P>
+         * If set to {@code true}, then the APN setting will persist to the modem.
+         * <p>Type: INTEGER (boolean)</p>
          *@hide
          */
+        @SystemApi
         public static final String MODEM_COGNITIVE = "modem_cognitive";
 
         /**
-         * The max connections of this apn
+         * The max connections of this APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
+        @SystemApi
         public static final String MAX_CONNS = "max_conns";
 
         /**
-         * The wait time for retry of the apn
+         * The wait time for retry of the APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
+        @SystemApi
         public static final String WAIT_TIME = "wait_time";
 
         /**
-         * The time to limit max connection for the apn
+         * The time to limit max connection for the APN.
          * <p>Type: INTEGER</p>
          *@hide
          */
+        @SystemApi
         public static final String MAX_CONNS_TIME = "max_conns_time";
 
         /**
-         * The MTU size of the mobile interface to  which the APN connected
+         * The MTU(Maxinum transmit unit) size of the mobile interface to which the APN connected.
          * <p>Type: INTEGER </p>
          * @hide
          */
+        @SystemApi
         public static final String MTU = "mtu";
 
         /**
-         * Is this APN added/edited/deleted by a user or carrier?
+         * APN edit status. APN could be added/edited/deleted by a user or carrier.
          * <p>Type: INTEGER </p>
          * @hide
          */
+        @SystemApi
         public static final String EDITED = "edited";
 
         /**
-         * Is this APN visible to the user?
-         * <p>Type: INTEGER (boolean) </p>
+         * {@code true} if this APN visible to the user, {@code false} otherwise.
+         * <p>Type: INTEGER (boolean)</p>
          * @hide
          */
+        @SystemApi
         public static final String USER_VISIBLE = "user_visible";
 
         /**
-         * Is the user allowed to edit this APN?
-         * <p>Type: INTEGER (boolean) </p>
+         * {@code true} if the user allowed to edit this APN, {@code false} otherwise.
+         * <p>Type: INTEGER (boolean)</p>
          * @hide
          */
+        @SystemApi
         public static final String USER_EDITABLE = "user_editable";
 
         /**
-         * Following are possible values for the EDITED field
+         * {@link #EDITED APN edit status} indicates that this APN has not been edited or fails to
+         * edit.
+         * <p>Type: INTEGER </p>
          * @hide
          */
+        @SystemApi
         public static final int UNEDITED = 0;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} indicates that this APN has been edited by users.
+         * <p>Type: INTEGER </p>
+         * @hide
          */
+        @SystemApi
         public static final int USER_EDITED = 1;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} indicates that this APN has been deleted by users.
+         * <p>Type: INTEGER </p>
+         * @hide
          */
+        @SystemApi
         public static final int USER_DELETED = 2;
+
         /**
-         * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
-         * by the user is still present in the new APN database and therefore must remain tagged
-         * as user deleted rather than completely removed from the database
+         * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+         * deleted by the user is still present in the new APN database and therefore must remain
+         * tagged as user deleted rather than completely removed from the database.
          * @hide
          */
         public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} indicates that this APN has been edited by carriers.
+         * <p>Type: INTEGER </p>
+         * @hide
          */
+        @SystemApi
         public static final int CARRIER_EDITED = 4;
+
         /**
-         * CARRIER_DELETED values are currently not used as there is no usecase. If they are used,
+         * {@link #EDITED APN edit status} indicates that this APN has been deleted by carriers.
+         * CARRIER_DELETED values are currently not used as there is no use case. If they are used,
          * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+         * <p>Type: INTEGER </p>
          * @hide
          */
         public static final int CARRIER_DELETED = 5;
+
         /**
-         *  @hide
+         * {@link #EDITED APN edit status} is an intermediate value used to indicate that an entry
+         * deleted by the carrier is still present in the new APN database and therefore must remain
+         * tagged as user deleted rather than completely removed from the database.
+         * @hide
          */
         public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
 
@@ -3011,16 +3043,20 @@
          * The APN set id. When the user manually selects an APN or the framework sets an APN as
          * preferred, all APNs with the same set id as the selected APN should be prioritized over
          * APNs in other sets.
+         * <p>Type: INTEGER</p>
          * @hide
          */
+        @SystemApi
         public static final String APN_SET_ID = "apn_set_id";
 
         /**
-         * Possible value for the APN_SET_ID field. By default APNs will not belong to a set. If the
-         * user manually selects an APN with no set set, there is no need to prioritize any specific
-         * APN set ids.
+         * Possible value for the{@link #APN_SET_ID} field. By default APNs will not belong to a
+         * set. If the user manually selects an APN with no set set, there is no need to prioritize
+         * any specific APN set ids.
+         * <p>Type: INTEGER</p>
          * @hide
          */
+        @SystemApi
         public static final int NO_SET_SET = 0;
 
     }
@@ -3524,6 +3560,18 @@
         public static final String CARRIER_ID = "carrier_id";
 
         /**
+         * A unique mno carrier id. mno carrier shares the same {@link All#MCCMNC} as carrier id
+         * and can be solely identified by {@link All#MCCMNC} only. If there is no such mno
+         * carrier, then mno carrier id equals to {@link #CARRIER_ID carrier id}.
+         *
+         * <p>mno carrier id can be used as fallback id. When the exact carrier id configurations
+         * are not found, usually fall back to its mno carrier id.
+         * <P>Type: INTEGER </P>
+         * @hide
+         */
+        public static final String MNO_CARRIER_ID = "mno_carrier_id";
+
+        /**
          * Contains mappings between matching rules with carrier id for all carriers.
          * @hide
          */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fac1943..b0997f1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1202,6 +1202,20 @@
             "always_show_data_rat_icon_bool";
 
     /**
+     * Boolean indicating if default data account should show LTE or 4G icon
+     * @hide
+     */
+    public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL =
+            "show_4g_for_lte_data_icon_bool";
+
+    /**
+     * Boolean indicating if lte+ icon should be shown if available
+     * @hide
+     */
+    public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL =
+            "hide_lte_plus_data_icon_bool";
+
+    /**
      * Boolean to decide whether to show precise call failed cause to user
      * @hide
      */
@@ -1965,6 +1979,31 @@
     public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
 
     /**
+     * Indicates if the carrier supports auto-upgrading a call to RTT when receiving a call from a
+     * RTT-supported device.
+     * @hide
+     */
+    public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool";
+
+    /**
+     * Indicates if the carrier supports RTT during a video call.
+     * @hide
+     */
+    public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+
+    /**
+     * Indicates if the carrier supports upgrading a voice call to an RTT call during the call.
+     * @hide
+     */
+    public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
+
+    /**
+     * Indicates if the carrier supports downgrading a RTT call to a voice call during the call.
+     * @hide
+     */
+    public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
+
+    /**
      * The flag to disable the popup dialog which warns the user of data charges.
      * @hide
      */
@@ -2505,6 +2544,8 @@
         sDefaults.putBoolean(KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL, false);
         sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, true);
+        sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
         sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
         sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 3ea018a..c83d6aa 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Handler;
@@ -291,6 +292,15 @@
      */
     public static final int LISTEN_PREFERRED_DATA_SUBID_CHANGE              = 0x00400000;
 
+    /**
+     *  Listen for changes to the radio power state.
+     *
+     *  @see #onRadioPowerStateChanged
+     *  @hide
+     */
+    @SystemApi
+    public static final int LISTEN_RADIO_POWER_STATE_CHANGED               = 0x00800000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -420,6 +430,9 @@
                     case LISTEN_PREFERRED_DATA_SUBID_CHANGE:
                         PhoneStateListener.this.onPreferredDataSubIdChanged((int) msg.obj);
                         break;
+                    case LISTEN_RADIO_POWER_STATE_CHANGED:
+                        PhoneStateListener.this.onRadioPowerStateChanged((int) msg.obj);
+                        break;
                 }
             }
         };
@@ -672,6 +685,17 @@
     }
 
     /**
+     * Callback invoked when modem radio power state changes. Requires
+     * the READ_PRIVILEGED_PHONE_STATE permission.
+     * @param state the modem radio power state
+     * @hide
+     */
+    @SystemApi
+    public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when telephony has received notice from a carrier
      * app that a network action that could result in connectivity loss
      * has been requested by an app using
@@ -807,6 +831,10 @@
             send(LISTEN_PREFERRED_DATA_SUBID_CHANGE, 0, 0, subId);
         }
 
+        public void onRadioPowerStateChanged(@TelephonyManager.RadioPowerState int state) {
+            send(LISTEN_RADIO_POWER_STATE_CHANGED, 0, 0, state);
+        }
+
     }
 
     /**
diff --git a/telephony/java/android/telephony/RcsManager.java b/telephony/java/android/telephony/RcsManager.java
new file mode 100644
index 0000000..00ce03a
--- /dev/null
+++ b/telephony/java/android/telephony/RcsManager.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.IRcs;
+
+/**
+ * RcsManager is the application interface to RcsProvider and provides access methods to
+ * RCS related database tables.
+ * @hide - TODO make this public
+ */
+public class RcsManager {
+    private static final String TAG = "RcsManager";
+    private static final boolean VDBG = false;
+
+    /**
+     * Delete the RcsThread identified by the given threadId.
+     * @param threadId threadId of the thread to be deleted.
+     */
+    public void deleteThread(int threadId) {
+        if (VDBG) logd("deleteThread: threadId: " + threadId);
+        try {
+            IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+            if (iRcs != null) {
+                iRcs.deleteThread(threadId);
+            }
+        } catch (RemoteException re) {
+
+        }
+    }
+
+    private static void logd(String msg) {
+        Rlog.d(TAG, msg);
+    }
+}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f850693..7f87c4d 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -88,8 +88,7 @@
     /** @hide */
     public static final int INVALID_PHONE_INDEX = -1;
 
-    /** An invalid slot identifier */
-    /** @hide */
+    /** Indicates invalid sim slot. This can be returned by {@link #getSlotIndex(int)}. */
     public static final int INVALID_SIM_SLOT_INDEX = -1;
 
     /** Indicates the default subscription ID in Telephony. */
@@ -1310,15 +1309,15 @@
 
     /**
      * Get slotIndex associated with the subscription.
-     * @return slotIndex as a positive integer or a negative value if an error either
-     * SIM_NOT_INSERTED or < 0 if an invalid slot index
-     * @hide
+     *
+     * @param subscriptionId the unique SubscriptionInfo index in database
+     * @return slotIndex as a positive integer or {@link #INVALID_SIM_SLOT_INDEX} if the supplied
+     * subscriptionId doesn't have an associated slot index.
      */
-    @UnsupportedAppUsage
-    public static int getSlotIndex(int subId) {
-        if (!isValidSubscriptionId(subId)) {
+    public static int getSlotIndex(int subscriptionId) {
+        if (!isValidSubscriptionId(subscriptionId)) {
             if (DBG) {
-                logd("[getSlotIndex]- fail");
+                logd("[getSlotIndex]- supplied subscriptionId is invalid.");
             }
         }
 
@@ -1327,7 +1326,7 @@
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                result = iSub.getSlotIndex(subId);
+                result = iSub.getSlotIndex(subscriptionId);
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2207,24 +2206,25 @@
     }
 
     /**
-     * Set preferred default data.
-     * Set on which slot most cellular data will be on.
-     * It's also usually what we set up internet connection on.
+     * Set which subscription is preferred for cellular data.
+     * It's also usually the subscription we set up internet connection on.
      *
      * PreferredData overwrites user setting of default data subscription. And it's used
-     * by AlternativeNetworkAccessService or carrier apps to switch primary and CBRS
+     * by AlternativeNetworkService or carrier apps to switch primary and CBRS
      * subscription dynamically in multi-SIM devices.
      *
-     * @param slotId which slot is preferred to for cellular data. If it's INVALID, it means
-     *               it's unset and defaultDataSubId is used to determine which modem is preferred.
+     * @param subId which subscription is preferred to for cellular data. If it's
+     *              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}, it means
+     *              it's unset and {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *              is used to determine which modem is preferred.
      * @hide
      *
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setPreferredData(int slotId) {
-        if (VDBG) logd("[setPreferredData]+ slotId:" + slotId);
+    public void setPreferredData(int subId) {
+        if (VDBG) logd("[setPreferredData]+ subId:" + subId);
         setSubscriptionPropertyHelper(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                "setPreferredData", (iSub)-> iSub.setPreferredData(slotId));
+                "setPreferredData", (iSub)-> iSub.setPreferredData(subId));
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fca14c8..69901d2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import static android.content.Context.TELECOM_SERVICE;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.IntDef;
@@ -59,6 +60,7 @@
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Log;
@@ -705,7 +707,7 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @Deprecated
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage
     public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
             "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
 
@@ -1162,6 +1164,16 @@
     public static final String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
 
     /**
+     * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
+     * the updated mno carrier id of the current subscription.
+     * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
+     * the carrier cannot be identified.
+     *
+     *@hide
+     */
+    public static final String EXTRA_MNO_CARRIER_ID = "android.telephony.extra.MNO_CARRIER_ID";
+
+    /**
      * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
      * indicates the updated carrier name of the current subscription.
      * {@see TelephonyManager#getSimCarrierIdName()}
@@ -1220,6 +1232,38 @@
      */
     public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
 
+     /**
+     * Broadcast intent action indicating that the telephony provider DB got lost.
+     *
+     * <p>
+     * The {@link #EXTRA_IS_CORRUPTED} extra indicates whether the database is lost
+     * due to corruption or not
+     *
+     * <p class="note">
+     * Requires the MODIFY_PHONE_STATE permission.
+     *
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     *
+     * @see #EXTRA_IS_CORRUPTED
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public static final String ACTION_MMSSMS_DATABASE_LOST =
+            "android.intent.action.MMSSMS_DATABASE_LOST";
+
+    /**
+     * A boolean extra used with {@link #ACTION_MMSSMS_DATABASE_LOST} to indicate
+     * whether the database is lost due to corruption or not.
+     *
+     * @see #ACTION_MMSSMS_DATABASE_LOST
+     *
+     * @hide
+     */
+    public static final String EXTRA_IS_CORRUPTED = "isCorrupted";
+
     //
     //
     // Device Info
@@ -1267,11 +1311,11 @@
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
      * or ESN for CDMA phones. Return null if device ID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
@@ -1296,11 +1340,11 @@
      * Returns the unique device ID of a subscription, for example, the IMEI for
      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which deviceID is returned
      *
@@ -1328,11 +1372,11 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // No support for device / profile owner.
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1344,11 +1388,11 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which IMEI is returned
      */
@@ -1397,11 +1441,11 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // No support for device / profile owner.
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1412,11 +1456,11 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
-     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or for the calling package to be the
-     * device or profile owner. The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, or for the calling package to be the
+     * device or profile owner and have the READ_PHONE_STATE permission. The profile owner is an app
+     * that owns a managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param slotIndex of which MEID is returned
      */
@@ -1618,8 +1662,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return null;
-            return telephony.getNeighboringCellInfo(mContext.getOpPackageName(),
-                    mContext.getApplicationInfo().targetSdkVersion);
+            return telephony.getNeighboringCellInfo(mContext.getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -1722,7 +1765,7 @@
     }
 
     /** {@hide} */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    @UnsupportedAppUsage
     private int getPhoneTypeFromProperty(int phoneId) {
         String type = getTelephonyProperty(phoneId,
                 TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
@@ -2913,11 +2956,11 @@
      * unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2930,11 +2973,11 @@
      * unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param subId for which Sim Serial number is returned
      * @hide
@@ -3075,11 +3118,11 @@
      * Return null if it is unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -3093,11 +3136,11 @@
      * Return null if it is unavailable.
      *
      * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
-     * profile owner, or that the calling app has carrier privileges (see {@link
-     * #hasCarrierPrivileges}). The profile owner is an app that owns a managed profile on the
-     * device; for more details see <a href="https://developer.android.com/work/managed-profiles">
-     * Work profiles</a>. Profile owner access is deprecated and will be removed in a future
-     * release.
+     * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
+     * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
+     * managed profile on the device; for more details see <a
+     * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
+     * access is deprecated and will be removed in a future release.
      *
      * @param subId whose subscriber id is returned
      * @hide
@@ -6870,6 +6913,60 @@
     }
 
     /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RADIO_POWER_"},
+            value = {RADIO_POWER_OFF,
+                    RADIO_POWER_ON,
+                    RADIO_POWER_UNAVAILABLE,
+            })
+    public @interface RadioPowerState {}
+
+    /**
+     * Radio explicitly powered off (e.g, airplane mode).
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_OFF = 0;
+
+    /**
+     * Radio power is on.
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_ON = 1;
+
+    /**
+     * Radio power unavailable (eg, modem resetting or not booted).
+     * @hide
+     */
+    @SystemApi
+    public static final int RADIO_POWER_UNAVAILABLE = 2;
+
+    /**
+     * @return current modem radio state.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+     * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE})
+    public @RadioPowerState int getRadioPowerState() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getRadioPowerState(getSlotIndex(), mContext.getOpPackageName());
+            }
+        } catch (RemoteException ex) {
+            // This could happen if binder process crashes.
+        }
+        return RADIO_POWER_UNAVAILABLE;
+    }
+
+    /** @hide */
     @SystemApi
     @SuppressLint("Doclava125")
     public void updateServiceLocation() {
@@ -7340,7 +7437,9 @@
     @UnsupportedAppUsage
     public boolean isVolteAvailable() {
         try {
-            return getITelephony().isVolteAvailable(getSubId());
+            return getITelephony().isAvailable(getSubId(),
+                    MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
         } catch (RemoteException | NullPointerException ex) {
             return false;
         }
@@ -8279,20 +8378,31 @@
     }
 
     /**
-     * Action set from carrier signalling broadcast receivers to enable/disable metered apns
-     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
-     * @param subId the subscription ID that this action applies to.
-     * @param enabled control enable or disable metered apns.
+     * Used to enable or disable carrier data by the system based on carrier signalling or
+     * carrier privileged apps. Different from {@link #setDataEnabled(boolean)} which is linked to
+     * user settings, carrier data on/off won't affect user settings but will bypass the
+     * settings and turns off data internally if set to {@code false}.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *
+     * @param enabled control enable or disable carrier data.
      * @hide
      */
-    public void carrierActionSetMeteredApnsEnabled(int subId, boolean enabled) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setCarrierDataEnabled(boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.carrierActionSetMeteredApnsEnabled(subId, enabled);
+                service.carrierActionSetMeteredApnsEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled);
             }
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#carrierActionSetMeteredApnsEnabled", e);
+            Log.e(TAG, "Error calling ITelephony#setCarrierDataEnabled", e);
         }
     }
 
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index eb144f9..aabefe3 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -257,7 +257,7 @@
 
     private final int mProfileId;
 
-    private final boolean mModemCognitive;
+    private final boolean mPersistent;
     private final int mMaxConns;
     private final int mWaitTime;
     private final int mMaxConnsTime;
@@ -290,13 +290,13 @@
     }
 
     /**
-     * Returns if the APN setting is to be set in modem.
+     * Returns if the APN setting is persistent on the modem.
      *
      * @return is the APN setting to be set in modem
      * @hide
      */
-    public boolean getModemCognitive() {
-        return mModemCognitive;
+    public boolean isPersistent() {
+        return mPersistent;
     }
 
     /**
@@ -616,7 +616,7 @@
         this.mCarrierEnabled = builder.mCarrierEnabled;
         this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
         this.mProfileId = builder.mProfileId;
-        this.mModemCognitive = builder.mModemCognitive;
+        this.mPersistent = builder.mModemCognitive;
         this.mMaxConns = builder.mMaxConns;
         this.mWaitTime = builder.mWaitTime;
         this.mMaxConnsTime = builder.mMaxConnsTime;
@@ -740,7 +740,7 @@
                 apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress,
                 apn.mMmsProxyPort, apn.mUser, apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask,
                 apn.mProtocol, apn.mRoamingProtocol, apn.mCarrierEnabled, apn.mNetworkTypeBitmask,
-                apn.mProfileId, apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime,
+                apn.mProfileId, apn.mPersistent, apn.mMaxConns, apn.mWaitTime,
                 apn.mMaxConnsTime, apn.mMtu, apn.mMvnoType, apn.mMvnoMatchData, apn.mApnSetId);
     }
 
@@ -947,7 +947,7 @@
         sb.append(", ").append(PROTOCOL_INT_MAP.get(mRoamingProtocol));
         sb.append(", ").append(mCarrierEnabled);
         sb.append(", ").append(mProfileId);
-        sb.append(", ").append(mModemCognitive);
+        sb.append(", ").append(mPersistent);
         sb.append(", ").append(mMaxConns);
         sb.append(", ").append(mWaitTime);
         sb.append(", ").append(mMaxConnsTime);
@@ -1029,7 +1029,7 @@
                 && Objects.equals(mMmsc, other.mMmsc)
                 && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
                 && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
-                && Objects.equals(mProxyPort,other.mProxyPort)
+                && Objects.equals(mProxyPort, other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
@@ -1038,7 +1038,7 @@
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
                 && Objects.equals(mProfileId, other.mProfileId)
-                && Objects.equals(mModemCognitive, other.mModemCognitive)
+                && Objects.equals(mPersistent, other.mPersistent)
                 && Objects.equals(mMaxConns, other.mMaxConns)
                 && Objects.equals(mWaitTime, other.mWaitTime)
                 && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
@@ -1080,11 +1080,11 @@
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
                 && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
-                && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
+                && (isDataRoaming || Objects.equals(mProtocol, other.mProtocol))
                 && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
                 && Objects.equals(mProfileId, other.mProfileId)
-                && Objects.equals(mModemCognitive, other.mModemCognitive)
+                && Objects.equals(mPersistent, other.mPersistent)
                 && Objects.equals(mMaxConns, other.mMaxConns)
                 && Objects.equals(mWaitTime, other.mWaitTime)
                 && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index e8597b2..da4822c 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -68,17 +68,15 @@
 
     private final int mMtu;
 
-    private final String mMvnoType;
+    private final boolean mPersistent;
 
-    private final String mMvnoMatchData;
+    private final boolean mPreferred;
 
-    private final boolean mModemCognitive;
-
-    public DataProfile(int profileId, String apn, String protocol, int authType,
-                String userName, String password, int type, int maxConnsTime, int maxConns,
-                int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
-                int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData,
-                boolean modemCognitive) {
+    /** @hide */
+    public DataProfile(int profileId, String apn, String protocol, int authType, String userName,
+                       String password, int type, int maxConnsTime, int maxConns, int waitTime,
+                       boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
+                       int bearerBitmap, int mtu, boolean persistent, boolean preferred) {
 
         this.mProfileId = profileId;
         this.mApn = apn;
@@ -100,11 +98,11 @@
         this.mRoamingProtocol = roamingProtocol;
         this.mBearerBitmap = bearerBitmap;
         this.mMtu = mtu;
-        this.mMvnoType = mvnoType;
-        this.mMvnoMatchData = mvnoMatchData;
-        this.mModemCognitive = modemCognitive;
+        this.mPersistent = persistent;
+        this.mPreferred = preferred;
     }
 
+    /** @hide */
     public DataProfile(Parcel source) {
         mProfileId = source.readInt();
         mApn = source.readString();
@@ -121,9 +119,8 @@
         mRoamingProtocol = source.readString();
         mBearerBitmap = source.readInt();
         mMtu = source.readInt();
-        mMvnoType = source.readString();
-        mMvnoMatchData = source.readString();
-        mModemCognitive = source.readBoolean();
+        mPersistent = source.readBoolean();
+        mPreferred = source.readBoolean();
     }
 
     /**
@@ -207,23 +204,17 @@
     public int getMtu() { return mMtu; }
 
     /**
-     * @return The MVNO type: possible values are "imsi", "gid", "spn".
+     * @return {@code true} if modem must persist this data profile.
      */
-    public String getMvnoType() { return mMvnoType; }
+    public boolean isPersistent() { return mPersistent; }
 
     /**
-     * @return The MVNO match data. For example,
-     * SPN: A MOBILE, BEN NL, ...
-     * IMSI: 302720x94, 2060188, ...
-     * GID: 4E, 33, ...
+     * @return {@code true} if this data profile was used to bring up the last default
+     * (i.e internet) data connection successfully.
      */
-    public String getMvnoMatchData() { return mMvnoMatchData; }
+    public boolean isPreferred() { return  mPreferred; }
 
-    /**
-     * @return True if the data profile was sent to the modem through setDataProfile earlier.
-     */
-    public boolean isModemCognitive() { return mModemCognitive; }
-
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
@@ -233,11 +224,11 @@
     public String toString() {
         return "DataProfile=" + mProfileId + "/" + mProtocol + "/" + mAuthType
                 + "/" + (Build.IS_USER ? "***/***/***" :
-                         (mApn + "/" + mUserName + "/" + mPassword))
-                + "/" + mType + "/" + mMaxConnsTime
-                + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/"
-                + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/"
-                + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive;
+                         (mApn + "/" + mUserName + "/" + mPassword)) + "/" + mType + "/"
+                + mMaxConnsTime + "/" + mMaxConns + "/"
+                + mWaitTime + "/" + mEnabled + "/" + mSupportedApnTypesBitmap + "/"
+                + mRoamingProtocol + "/" + mBearerBitmap + "/" + mMtu + "/" + mPersistent + "/"
+                + mPreferred;
     }
 
     @Override
@@ -246,6 +237,7 @@
         return (o == this || toString().equals(o.toString()));
     }
 
+    /** @hide */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mProfileId);
@@ -263,11 +255,11 @@
         dest.writeString(mRoamingProtocol);
         dest.writeInt(mBearerBitmap);
         dest.writeInt(mMtu);
-        dest.writeString(mMvnoType);
-        dest.writeString(mMvnoMatchData);
-        dest.writeBoolean(mModemCognitive);
+        dest.writeBoolean(mPersistent);
+        dest.writeBoolean(mPreferred);
     }
 
+    /** @hide */
     public static final Parcelable.Creator<DataProfile> CREATOR =
             new Parcelable.Creator<DataProfile>() {
         @Override
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index d6a08543..bdba8c8 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -33,7 +33,7 @@
  * A parcelable class that wraps and retrieves the information of number, service category(s) and
  * country code for a specific emergency number.
  */
-public final class EmergencyNumber implements Parcelable {
+public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNumber> {
 
     private static final String LOG_TAG = "EmergencyNumber";
 
@@ -235,20 +235,22 @@
     }
 
     /**
-     * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of
-     * the emergency number.
+     * Returns the bitmask of emergency service categories of the emergency number.
      *
-     * @return bitmask of the emergency service categories {@link EmergencyServiceCategories}
+     * @return bitmask of the emergency service categories
      */
     public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
         return mEmergencyServiceCategoryBitmask;
     }
 
     /**
-     * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency
-     * number.
+     * Returns the emergency service categories of the emergency number.
      *
-     * @return a list of the emergency service categories {@link EmergencyServiceCategories}
+     * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in
+     * all categories.
+     *
+     * @return a list of the emergency service categories
      */
     public List<Integer> getEmergencyServiceCategories() {
         List<Integer> categories = new ArrayList<>();
@@ -276,34 +278,37 @@
     }
 
     /**
-     * Checks if the emergency number is in the specified emergency service category(s)
-     * {@link EmergencyServiceCategories}.
+     * Checks if the emergency number is in the supplied emergency service category(s).
+     *
+     * @param categories - the supplied emergency service categories
      *
      * @return {@code true} if the emergency number is in the specified emergency service
-     * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise.
-     *
-     * @param categories - emergency service categories {@link EmergencyServiceCategories}
+     * category(s) or if its emergency service category is
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
      */
     public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
         if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
             return serviceUnspecified();
         }
+        if (serviceUnspecified()) {
+            return true;
+        }
         return (mEmergencyServiceCategoryBitmask & categories) == categories;
     }
 
     /**
-     * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number.
+     * Returns the bitmask of the sources of the emergency number.
      *
-     * @return bitmask of the emergency number sources {@link EmergencyNumberSources}
+     * @return bitmask of the emergency number sources
      */
     public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
         return mEmergencyNumberSourceBitmask;
     }
 
     /**
-     * Returns a list of {@link EmergencyNumberSources} of the emergency number.
+     * Returns a list of sources of the emergency number.
      *
-     * @return a list of {@link EmergencyNumberSources}
+     * @return a list of emergency number sources
      */
     public List<Integer> getEmergencyNumberSources() {
         List<Integer> sources = new ArrayList<>();
@@ -316,13 +321,12 @@
     }
 
     /**
-     * Checks if the emergency number is from the specified emergency number source(s)
-     * {@link EmergencyNumberSources}.
+     * Checks if the emergency number is from the specified emergency number source(s).
      *
      * @return {@code true} if the emergency number is from the specified emergency number
-     * source(s) {@link EmergencyNumberSources}; {@code false} otherwise.
+     * source(s); {@code false} otherwise.
      *
-     * @param sources - {@link EmergencyNumberSources}
+     * @param sources - the supplied emergency number sources
      */
     public boolean isFromSources(@EmergencyNumberSources int sources) {
         return (mEmergencyNumberSourceBitmask & sources) == sources;
@@ -359,6 +363,62 @@
         return (o == this || toString().equals(o.toString()));
     }
 
+    /**
+     * Calculate the score for display priority.
+     *
+     * A higher display priority score means the emergency number has a higher display priority.
+     * The score is higher if the source is defined for a higher display priority.
+     *
+     * The priority of sources are defined as follows:
+     *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
+     *     EMERGENCY_NUMBER_SOURCE_SIM >
+     *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
+     *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
+     *
+     */
+    private int getDisplayPriorityScore() {
+        int score = 0;
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) {
+            score += 1 << 4;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
+            score += 1 << 3;
+        }
+        // TODO add a score if the number comes from Google's emergency number database
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
+            score += 1 << 1;
+        }
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) {
+            score += 1 << 0;
+        }
+        return score;
+    }
+
+    /**
+     * Compare the display priority for this emergency number and the supplied emergency number.
+     *
+     * @param emergencyNumber the supplied emergency number
+     * @return a negative value if the supplied emergency number has a lower display priority;
+     *         a positive value if the supplied emergency number has a higher display priority;
+     *         0 if both have equal display priority.
+     */
+    @Override
+    public int compareTo(EmergencyNumber emergencyNumber) {
+        if (this.getDisplayPriorityScore()
+                > emergencyNumber.getDisplayPriorityScore()) {
+            return -1;
+        } else if (this.getDisplayPriorityScore()
+                < emergencyNumber.getDisplayPriorityScore()) {
+            return 1;
+        } else {
+            /**
+             * TODO if both numbers have the same display priority score, the number matches the
+             * Google's emergency number database has a higher display priority.
+             */
+            return 0;
+        }
+    }
+
     public static final Parcelable.Creator<EmergencyNumber> CREATOR =
             new Parcelable.Creator<EmergencyNumber>() {
         @Override
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index 8d18ae8..f2d0cbf 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -16,22 +16,19 @@
 
 package android.telephony.ims;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telecom.Log;
 import android.telephony.Rlog;
 
-/*
- * This file contains all the api's through which
- * information received in Dialog Event Package can be
- * queried
- */
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
- * Parcelable object to handle MultiEndpoint Dialog Information
+ * Parcelable object to handle MultiEndpoint Dialog Event Package Information.
  * @hide
  */
 @SystemApi
@@ -40,8 +37,39 @@
     private static final String TAG = "ImsExternalCallState";
 
     // Dialog States
+    /**
+     * The external call is in the confirmed dialog state.
+     */
     public static final int CALL_STATE_CONFIRMED = 1;
+    /**
+     * The external call is in the terminated dialog state.
+     */
     public static final int CALL_STATE_TERMINATED = 2;
+
+    /**@hide*/
+    @IntDef(flag = true,
+            value = {
+                    CALL_STATE_CONFIRMED,
+                    CALL_STATE_TERMINATED
+            },
+            prefix = "CALL_STATE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ExternalCallState {}
+
+    /**@hide*/
+    @IntDef(flag = true,
+            value = {
+                    ImsCallProfile.CALL_TYPE_VOICE,
+                    ImsCallProfile.CALL_TYPE_VT_TX,
+                    ImsCallProfile.CALL_TYPE_VT_RX,
+                    ImsCallProfile.CALL_TYPE_VT
+            },
+            prefix = "CALL_TYPE_")
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ExternalCallType {}
+
+
+
     // Dialog Id
     private int mCallId;
     // Number
@@ -58,10 +86,9 @@
     public ImsExternalCallState() {
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public ImsExternalCallState(int callId, Uri address, boolean isPullable, int callState,
-            int callType, boolean isCallheld) {
+    /**@hide*/
+    public ImsExternalCallState(int callId, Uri address, boolean isPullable,
+            @ExternalCallState int callState, int callType, boolean isCallheld) {
         mCallId = callId;
         mAddress = address;
         mIsPullable = isPullable;
@@ -71,9 +98,10 @@
         Rlog.d(TAG, "ImsExternalCallState = " + this);
     }
 
-    /** @hide */
+    /**@hide*/
     public ImsExternalCallState(int callId, Uri address, Uri localAddress,
-            boolean isPullable, int callState, int callType, boolean isCallheld) {
+            boolean isPullable, @ExternalCallState int callState, int callType,
+            boolean isCallheld) {
         mCallId = callId;
         mAddress = address;
         mLocalAddress = localAddress;
@@ -84,6 +112,31 @@
         Rlog.d(TAG, "ImsExternalCallState = " + this);
     }
 
+    /**
+     * Create a new ImsExternalCallState instance to contain Multiendpoint Dialog information.
+     * @param callId The unique ID of the call, which will be used to identify this external
+     *               connection.
+     * @param address A {@link Uri} containing the remote address of this external connection.
+     * @param localAddress A {@link Uri} containing the local address information.
+     * @param isPullable A flag determining if this external connection can be pulled to the current
+     *         device.
+     * @param callState The state of the external call.
+     * @param callType The type of external call.
+     * @param isCallheld A flag determining if the external connection is currently held.
+     */
+    public ImsExternalCallState(String callId, Uri address, Uri localAddress,
+            boolean isPullable, @ExternalCallState int callState, @ExternalCallType int callType,
+            boolean isCallheld) {
+        mCallId = getIdForString(callId);
+        mAddress = address;
+        mLocalAddress = localAddress;
+        mIsPullable = isPullable;
+        mCallState = callState;
+        mCallType = callType;
+        mIsHeld = isCallheld;
+        Rlog.d(TAG, "ImsExternalCallState = " + this);
+    }
+
     /** @hide */
     public ImsExternalCallState(Parcel in) {
         mCallId = in.readInt();
@@ -135,7 +188,9 @@
         return mAddress;
     }
 
-    /** @hide */
+    /**
+     * @return A {@link Uri} containing the local address from the Multiendpoint Dialog Information.
+     */
     public Uri getLocalAddress() {
         return mLocalAddress;
     }
@@ -144,11 +199,11 @@
         return mIsPullable;
     }
 
-    public int getCallState() {
+    public @ExternalCallState int getCallState() {
         return mCallState;
     }
 
-    public int getCallType() {
+    public @ExternalCallType int getCallType() {
         return mCallType;
     }
 
@@ -166,4 +221,15 @@
                 ", mCallType = " + mCallType +
                 ", mIsHeld = " + mIsHeld + "}";
     }
+
+    private int getIdForString(String idString) {
+        try {
+            return Integer.parseInt(idString);
+        } catch (NumberFormatException e) {
+            // In the case that there are alphanumeric characters, we will create a hash of the
+            // String value as a backup.
+            // TODO: Modify call IDs to use Strings as keys instead of integers in telephony/telecom
+            return idString.hashCode();
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
new file mode 100644
index 0000000..c9cf473
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -0,0 +1,760 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
+ * subscription.
+ *
+ * Allows a user to query the IMS MmTel feature information for a subscription, register for
+ * registration and MmTel capability status callbacks, as well as query/modify user settings for the
+ * associated subscription.
+ *
+ * @see #createForSubscriptionId(Context, int)
+ * @hide
+ */
+public class ImsMmTelManager {
+
+    private static final String TAG = "ImsMmTelManager";
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "WIFI_MODE_", value = {
+            WIFI_MODE_WIFI_ONLY,
+            WIFI_MODE_CELLULAR_PREFERRED,
+            WIFI_MODE_WIFI_PREFERRED
+            })
+    public @interface WiFiCallingMode {}
+
+    /**
+     * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
+     * registration if signal quality degrades.
+     * @hide
+     */
+    @SystemApi
+    public static final int WIFI_MODE_WIFI_ONLY = 0;
+
+    /**
+     * Prefer registering for IMS over LTE if LTE signal quality is high enough.
+     * @hide
+     */
+    @SystemApi
+    public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
+
+    /**
+     * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
+     * @hide
+     */
+    @SystemApi
+    public static final int WIFI_MODE_WIFI_PREFERRED = 2;
+
+    /**
+     * Callback class for receiving Registration callback events.
+     * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     */
+    public static class RegistrationCallback {
+
+        private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+
+            private final RegistrationCallback mLocalCallback;
+            private Executor mExecutor;
+
+            RegistrationBinder(RegistrationCallback localCallback) {
+                mLocalCallback = localCallback;
+            }
+
+            @Override
+            public void onRegistered(int imsRadioTech) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+            }
+
+            @Override
+            public void onRegistering(int imsRadioTech) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+            }
+
+            @Override
+            public void onDeregistered(ImsReasonInfo info) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onDeregistered(info)));
+            }
+
+            @Override
+            public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() ->
+                                mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+            }
+
+            @Override
+            public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() ->
+                                mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final RegistrationBinder mBinder = new RegistrationBinder(this);
+
+        /**
+         * Notifies the framework when the IMS Provider is registered to the IMS network.
+         *
+         * @param imsRadioTech the radio access technology. Valid values are defined in
+         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         */
+        public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is trying to register the IMS network.
+         *
+         * @param imsRadioTech the radio access technology. Valid values are defined in
+         * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+         */
+        public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         */
+        public void onDeregistered(ImsReasonInfo info) {
+        }
+
+        /**
+         * A failure has occurred when trying to handover registration to another technology type,
+         * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+         *
+         * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
+         *         failed
+         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+         */
+        public void onTechnologyChangeFailed(
+                @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+        }
+
+        /**
+         * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
+         * it changes. Per RFC3455, an associated URI is a URI that the service provider has
+         * allocated to a user for their own usage. A user's phone number is typically one of the
+         * associated URIs.
+         * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
+         *         subscription.
+         * @hide
+         */
+        public void onSubscriberAssociatedUriChanged(Uri[] uris) {
+        }
+
+        /**@hide*/
+        public final IImsRegistrationCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        //Only exposed as public for compatibility with deprecated ImsManager APIs.
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    /**
+     * Receives IMS capability status updates from the ImsService.
+     *
+     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     */
+    public static class CapabilityCallback {
+
+        private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+
+            private final CapabilityCallback mLocalCallback;
+            private Executor mExecutor;
+
+            CapabilityBinder(CapabilityCallback c) {
+                mLocalCallback = c;
+            }
+
+            @Override
+            public void onCapabilitiesStatusChanged(int config) {
+                if (mLocalCallback == null) return;
+
+                Binder.withCleanCallingIdentity(() ->
+                        mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
+                                new MmTelFeature.MmTelCapabilities(config))));
+            }
+
+            @Override
+            public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                    boolean isEnabled) {
+                // This is not used for public interfaces.
+            }
+
+            @Override
+            public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                    @ImsFeature.ImsCapabilityError int reason) {
+                // This is not used for public interfaces
+            }
+
+            private void setExecutor(Executor executor) {
+                mExecutor = executor;
+            }
+        }
+
+        private final CapabilityBinder mBinder = new CapabilityBinder(this);
+
+        /**
+         * The status of the feature's capabilities has changed to either available or unavailable.
+         * If unavailable, the feature is not able to support the unavailable capability at this
+         * time.
+         *
+         * @param capabilities The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(
+                MmTelFeature.MmTelCapabilities capabilities) {
+        }
+
+        /**@hide*/
+        public final IImsCapabilityCallback getBinder() {
+            return mBinder;
+        }
+
+        /**@hide*/
+        // Only exposed as public method for compatibility with deprecated ImsManager APIs.
+        // TODO: clean up dependencies and change back to private visibility.
+        public void setExecutor(Executor executor) {
+            mBinder.setExecutor(executor);
+        }
+    }
+
+    private Context mContext;
+    private int mSubId;
+
+    /**
+     * Create an instance of ImsManager for the subscription id specified.
+     *
+     * @param context
+     * @param subId The ID of the subscription that this ImsManager will use.
+     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+     * @throws IllegalArgumentException if the subscription is invalid or
+     *         the subscription ID is not an active subscription.
+     */
+    public static ImsMmTelManager createForSubscriptionId(Context context, int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)
+                || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription ID");
+        }
+
+        return new ImsMmTelManager(context, subId);
+    }
+
+    private ImsMmTelManager(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+    }
+
+    /**
+     * Registers a {@link RegistrationCallback} with the system, which will provide registration
+     * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
+     * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+     * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+     * after a subscription is removed.
+     * @param executor The executor the callback events should be run on.
+     * @param c The {@link RegistrationCallback} to be added.
+     * @see #removeImsRegistrationCallback(RegistrationCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+            @NonNull RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+        try {
+            getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Removes an existing {@link RegistrationCallback}. Ensure to call this method when cleaning
+     * up to avoid memory leaks or when the subscription is removed.
+     * @param c The {@link RegistrationCallback} to be removed.
+     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        try {
+            getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Registers a {@link CapabilityCallback} with the system, which will provide MmTel capability
+     * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
+     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
+     * subscription changed events and call
+     * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
+     * is removed.
+     * @param executor The executor the callback events should be run on.
+     * @param c The MmTel {@link CapabilityCallback} to be registered.
+     * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+            @NonNull CapabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Must include a non-null Executor.");
+        }
+        c.setExecutor(executor);
+        try {
+            getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
+     * up to avoid memory leaks.
+     * @param c The MmTel {@link CapabilityCallback} to be removed.
+     * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+        if (c == null) {
+            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
+        }
+        try {
+            getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the user's setting for whether or not to use MmTel capabilities over IMS,
+     * such as voice and video, depending on carrier configuration for the current subscription.
+     * @see #setAdvancedCallingSetting(boolean)
+     * @return true if the user’s setting for advanced calling is enabled and false otherwise.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isAdvancedCallingSettingEnabled() {
+        try {
+            return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+     * enable MmTel IMS features, such as voice and video calling, depending on the carrier
+     * configuration for the current subscription. Modifying this value may also trigger an IMS
+     * registration or deregistration, depending on the new value.
+     * @see #isAdvancedCallingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setAdvancedCallingSetting(boolean isEnabled) {
+        try {
+            getITelephony().setAdvancedCallingSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the IMS MmTel capability for a given registration technology. This does not
+     * necessarily mean that we are registered and the capability is available, but rather the
+     * subscription is capable of this service over IMS.
+     *
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
+     * @see #isAvailable(int, int)
+     *
+     * @param imsRegTech The IMS registration technology, can be one of the following:
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     * @param capability The IMS MmTel capability to query, can be one of the following:
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+     * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
+     *         otherwise.
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        try {
+            return getITelephony().isCapable(mSubId, capability, imsRegTech,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Query the availability of an IMS MmTel capability for a given registration technology. If
+     * a capability is available, IMS is registered and the service is currently available over IMS.
+     *
+     * @see #isCapable(int, int)
+     *
+     * @param imsRegTech The IMS registration technology, can be one of the following:
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
+     *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
+     * @param capability The IMS MmTel capability to query, can be one of the following:
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
+     *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+     * @return {@code true} if the MmTel IMS capability is available for this subscription, false
+     *         otherwise.
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
+        try {
+            return getITelephony().isAvailable(mSubId, capability, imsRegTech,
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * The user's setting for whether or not they have enabled the "Video Calling" setting.
+     * @return true if the user’s “Video Calling” setting is currently enabled.
+     * @see #setVtSetting(boolean)
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public boolean isVtSettingEnabled() {
+        try {
+            return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for Video Telephony and enable the Video Telephony capability.
+     * @see #isVtSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVtSetting(boolean isEnabled) {
+        try {
+            getITelephony().setVtSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
+     * @see #setVoWiFiSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isVoWiFiSettingEnabled() {
+        try {
+            return getITelephony().isVoWiFiSettingEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sets the user's setting for whether or not Voice over WiFi is enabled.
+     * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
+     * @see #isVoWiFiSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiSetting(boolean isEnabled) {
+        try {
+            getITelephony().setVoWiFiSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
+     * if disabled.
+     * @see #setVoWiFiRoamingSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isVoWiFiRoamingSettingEnabled() {
+        try {
+            return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for Voice over WiFi while roaming.
+     * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
+     *     false otherwise.
+     * @see #isVoWiFiRoamingSettingEnabled()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiRoamingSetting(boolean isEnabled) {
+        try {
+            getITelephony().setVoWiFiRoamingSetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
+     * Typically used during the Voice over WiFi registration process for some carriers.
+     *
+     * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
+     *     otherwise.
+     * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #setVoWiFiSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
+        try {
+            getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return The Voice over WiFi Mode preference set by the user, which can be one of the
+     * following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #setVoWiFiSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @WiFiCallingMode int getVoWiFiModeSetting() {
+        try {
+            return getITelephony().getVoWiFiModeSetting(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi calling mode.
+     * @param mode The user's preference for the technology to register for IMS over, can be one of
+     *    the following:
+     * - {@link #WIFI_MODE_WIFI_ONLY}
+     * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     * - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #getVoWiFiModeSetting()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
+        try {
+            getITelephony().setVoWiFiModeSetting(mSubId, mode);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
+     * another network.
+     *
+     * @return The user's preference for the technology to register for IMS over when roaming on
+     *     another network, can be one of the following:
+     *     - {@link #WIFI_MODE_WIFI_ONLY}
+     *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #setVoWiFiRoamingSetting(boolean)
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+        try {
+            return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Set the user's preference for Voice over WiFi mode while the device is roaming on another
+     * network.
+     *
+     * @param mode The user's preference for the technology to register for IMS over when roaming on
+     *     another network, can be one of the following:
+     *     - {@link #WIFI_MODE_WIFI_ONLY}
+     *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
+     *     - {@link #WIFI_MODE_WIFI_PREFERRED}
+     * @see #getVoWiFiRoamingModeSetting()
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
+        try {
+            getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Change the user's setting for RTT capability of this device.
+     * @param isEnabled if true RTT will be enabled during calls.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setRttCapabilitySetting(boolean isEnabled) {
+        try {
+            getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
+            return;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * @return true if TTY over VoLTE is supported
+     * @see android.telecom.TelecomManager#getCurrentTtyMode
+     * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    boolean isTtyOverVolteEnabled() {
+        try {
+            return getITelephony().isTtyOverVolteEnabled(mSubId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    private static SubscriptionManager getSubscriptionManager(Context context) {
+        SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
+        if (manager == null) {
+            throw new RuntimeException("Could not find SubscriptionManager.");
+        }
+        return manager;
+    }
+
+    private static ITelephony getITelephony() {
+        ITelephony binder = ITelephony.Stub.asInterface(
+                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+        if (binder == null) {
+            throw new RuntimeException("Could not find Telephony Service.");
+        }
+        return binder;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 4f37caa..749b191 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
@@ -23,7 +23,7 @@
 import android.telephony.ims.ImsReasonInfo;
 
 /**
- * See ImsRegistrationImplBase.Callback for more information.
+ * See {@link ImsManager#RegistrationCallback} for more information.
  *
  * {@hide}
  */
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index b77881e..7f69f43 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -167,59 +167,6 @@
      */
     public static final int CAPABILITY_SUCCESS = 0;
 
-
-    /**
-     * The framework implements this callback in order to register for Feature Capability status
-     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
-     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
-     * callbacks when the ImsService can not change the capability as requested, via
-     * {@link #onChangeCapabilityConfigurationError}.
-     *
-     * @hide
-     */
-    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
-
-        @Override
-        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
-            onCapabilitiesStatusChanged(new Capabilities(config));
-        }
-
-        /**
-         * Returns the result of a query for the capability configuration of a requested capability.
-         *
-         * @param capability The capability that was requested.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param isEnabled true if the capability is enabled, false otherwise.
-         */
-        @Override
-        public void onQueryCapabilityConfiguration(int capability, int radioTech,
-                boolean isEnabled) {
-
-        }
-
-        /**
-         * Called when a change to the capability configuration has returned an error.
-         *
-         * @param capability The capability that was requested to be changed.
-         * @param radioTech The IMS radio technology associated with the capability.
-         * @param reason error associated with the failure to change configuration.
-         */
-        @Override
-        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
-                @ImsCapabilityError int reason) {
-        }
-
-        /**
-         * The status of the feature's capabilities has changed to either available or unavailable.
-         * If unavailable, the feature is not able to support the unavailable capability at this
-         * time.
-         *
-         * @param config The new availability of the capabilities.
-         */
-        public void onCapabilitiesStatusChanged(Capabilities config) {
-        }
-    }
-
     /**
      * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
      * provided.
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 7681aef..9699594 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -17,6 +17,8 @@
 package android.telephony.ims.feature;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Message;
@@ -222,21 +224,31 @@
      * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
      * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
      * status can also be queried using {@link #queryCapabilityStatus()}.
+     * @see #isCapable(int)
      */
     public static class MmTelCapabilities extends Capabilities {
 
         /**
-         * @hide
+         * Create a new empty {@link MmTelCapabilities} instance.
+         * @see #addCapabilities(int)
+         * @see #removeCapabilities(int)
          */
         @VisibleForTesting
         public MmTelCapabilities() {
             super();
         }
 
+        /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/
+        @Deprecated
         public MmTelCapabilities(Capabilities c) {
             mCapabilities = c.mCapabilities;
         }
 
+        /**
+         * Create a new {link @MmTelCapabilities} instance with the provided capabilities.
+         * @param capabilities The capabilities that are supported for MmTel in the form of a
+         *                     bitfield.
+         */
         public MmTelCapabilities(int capabilities) {
             mCapabilities = capabilities;
         }
@@ -406,7 +418,10 @@
      * support the capability that is enabled. A capability that is disabled by the framework (via
      * {@link #changeEnabledCapabilities}) should also show the status as disabled.
      */
-    public final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+    public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
+        if (c == null) {
+            throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
+        }
         super.notifyCapabilitiesStatusChanged(c);
     }
 
@@ -414,7 +429,12 @@
      * Notify the framework of an incoming call.
      * @param c The {@link ImsCallSessionImplBase} of the new incoming call.
      */
-    public final void notifyIncomingCall(ImsCallSessionImplBase c, Bundle extras) {
+    public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
+            @NonNull Bundle extras) {
+        if (c == null || extras == null) {
+            throw new IllegalArgumentException("ImsCallSessionImplBase and Bundle can not be "
+                    + "null.");
+        }
         synchronized (mLock) {
             if (mListener == null) {
                 throw new IllegalStateException("Session is not available.");
@@ -434,7 +454,12 @@
      *        This can be null if no call information is available for the rejected call.
      * @param reason The {@link ImsReasonInfo} call rejection reason.
      */
-    public final void notifyRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
+    public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
+            @NonNull ImsReasonInfo reason) {
+        if (callProfile == null || reason == null) {
+            throw new IllegalArgumentException("ImsCallProfile and ImsReasonInfo must not be "
+                    + "null.");
+        }
         synchronized (mLock) {
             if (mListener == null) {
                 throw new IllegalStateException("Session is not available.");
@@ -508,8 +533,8 @@
      * the framework.
      */
     @Override
-    public void changeEnabledCapabilities(CapabilityChangeRequest request,
-            CapabilityCallbackProxy c) {
+    public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
+            @NonNull CapabilityCallbackProxy c) {
         // Base implementation, no-op
     }
 
@@ -531,7 +556,7 @@
      *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
      * @return a {@link ImsCallProfile} object
      */
-    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+    public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
         // Base Implementation - Should be overridden
         return null;
     }
@@ -552,7 +577,7 @@
      *
      * @param profile a call profile to make the call
      */
-    public ImsCallSessionImplBase createCallSession(ImsCallProfile profile) {
+    public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
         // Base Implementation - Should be overridden
         return null;
     }
@@ -569,7 +594,7 @@
      * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
      *        call will be placed over IMS or via CSFB.
      */
-    public @ProcessCallResult int shouldProcessCall(String[] numbers) {
+    public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
         return PROCESS_CALL_IMS;
     }
 
@@ -602,7 +627,7 @@
      * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
      * configuration.
      */
-    public ImsUtImplBase getUt() {
+    public @NonNull ImsUtImplBase getUt() {
         // Base Implementation - Should be overridden
         return new ImsUtImplBase();
     }
@@ -611,7 +636,7 @@
      * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
      * calls that support it.
      */
-    public ImsEcbmImplBase getEcbm() {
+    public @NonNull ImsEcbmImplBase getEcbm() {
         // Base Implementation - Should be overridden
         return new ImsEcbmImplBase();
     }
@@ -620,7 +645,7 @@
      * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
      * package processing for multi-endpoint.
      */
-    public ImsMultiEndpointImplBase getMultiEndpoint() {
+    public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
         // Base Implementation - Should be overridden
         return new ImsMultiEndpointImplBase();
     }
@@ -646,7 +671,7 @@
      *     }
      * }
      */
-    public void setUiTtyMode(int mode, Message onCompleteMessage) {
+    public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
         // Base Implementation - Should be overridden
     }
 
@@ -680,7 +705,7 @@
      * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
      * Provider.
      */
-    public ImsSmsImplBase getSmsImplementation() {
+    public @NonNull ImsSmsImplBase getSmsImplementation() {
         return new ImsSmsImplBase();
     }
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index cecf2e2..a08e031 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -76,64 +76,6 @@
     private static final int REGISTRATION_STATE_REGISTERING = 1;
     private static final int REGISTRATION_STATE_REGISTERED = 2;
 
-    /**
-     * Callback class for receiving Registration callback events.
-     * @hide
-     */
-    public static class Callback extends IImsRegistrationCallback.Stub {
-        /**
-         * Notifies the framework when the IMS Provider is connected to the IMS network.
-         *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationTech}.
-         */
-        @Override
-        public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
-        }
-
-        /**
-         * Notifies the framework when the IMS Provider is trying to connect the IMS network.
-         *
-         * @param imsRadioTech the radio access technology. Valid values are defined in
-         * {@link ImsRegistrationTech}.
-         */
-        @Override
-        public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
-        }
-
-        /**
-         * Notifies the framework when the IMS Provider is disconnected from the IMS network.
-         *
-         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
-         */
-        @Override
-        public void onDeregistered(ImsReasonInfo info) {
-        }
-
-        /**
-         * A failure has occurred when trying to handover registration to another technology type,
-         * defined in {@link ImsRegistrationTech}
-         *
-         * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
-         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
-         */
-        @Override
-        public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
-                ImsReasonInfo info) {
-        }
-
-        /**
-         * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
-         * it changes.
-         * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
-         *         subscription.
-         */
-        @Override
-        public void onSubscriberAssociatedUriChanged(Uri[] uris) {
-
-        }
-    }
-
     private final IImsRegistration mBinder = new IImsRegistration.Stub() {
 
         @Override
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 86cb1b7..b0c875e 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -100,6 +100,7 @@
     public static final int EVENT_DATA_RECONNECT = BASE + 47;
     public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
+    public static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = BASE + 50;
 
     /***** Constants *****/
 
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 38a1bc7..9e42f12 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -53,5 +53,6 @@
     void onUserMobileDataStateChanged(in boolean enabled);
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onPreferredDataSubIdChanged(in int subId);
+    void onRadioPowerStateChanged(in int state);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/IRcs.aidl b/telephony/java/com/android/internal/telephony/IRcs.aidl
new file mode 100644
index 0000000..ede8695
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IRcs.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+interface IRcs {
+    void deleteThread(int threadId);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 0ccd748..85b4941 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony;
 
-import android.app.PendingIntent;
 import android.telephony.SubscriptionInfo;
 
 interface ISub {
@@ -175,14 +174,14 @@
     int setParentSubId(int parentSubId, int subId);
 
     /**
-     * Set preferred default data.
-     * Set on which slot default data will be on.
+     * Set which subscription is preferred for cellular data. It's
+     * designed to overwrite default data subscription temporarily.
      *
-     * @param slotId which slot is preferred to for cellular data.
+     * @param subId which subscription is preferred to for cellular data.
      * @hide
      *
      */
-    int setPreferredData(int slotId);
+    int setPreferredData(int subId);
 
     /**
      * Get User downloaded Profiles.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 32eb12b..dc23358 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -38,10 +38,12 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyHistogram;
 import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.OperatorInfo;
@@ -280,7 +282,7 @@
     /**
      * Returns the neighboring cell information of the device.
      */
-    List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg, int targetSdk);
+    List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
 
      int getCallState();
 
@@ -1057,11 +1059,6 @@
      */
     boolean isWifiCallingAvailable(int subId);
 
-    /**
-     * Returns the Status of VoLTE for the subscription ID specified.
-     */
-    boolean isVolteAvailable(int subId);
-
      /**
      * Returns the Status of VT (video telephony) for the subscription ID specified.
      */
@@ -1499,4 +1496,123 @@
      * Set the default SMS app to a given package on a given user.
      */
     void setDefaultSmsApp(int userId, String packageName);
+
+    /**
+     * Return the modem radio power state for slot index.
+     *
+     */
+    int getRadioPowerState(int slotIndex, String callingPackage);
+
+    // IMS specific AIDL commands, see ImsMmTelManager.java
+
+    /**
+     * Adds an IMS registration status callback for the subscription id specified.
+     */
+    oneway void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+            String callingPackage);
+     /**
+      * Removes an existing IMS registration status callback for the subscription specified.
+      */
+    oneway void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
+            String callingPackage);
+
+    /**
+     * Adds an IMS MmTel capabilities callback for the subscription specified.
+     */
+    oneway void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+            String callingPackage);
+
+    /**
+     * Removes an existing IMS MmTel capabilities callback for the subscription specified.
+     */
+    oneway void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
+            String callingPackage);
+
+    /**
+     * return true if the IMS MmTel capability for the given registration tech is capable.
+     */
+    boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+
+    /**
+     * return true if the IMS MmTel capability for the given registration tech is available.
+     */
+    boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+
+    /**
+     * Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
+     */
+    boolean isAdvancedCallingSettingEnabled(int subId);
+
+    /**
+     * Modify the user's setting for whether or not 4G LTE is enabled.
+     */
+    void setAdvancedCallingSetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if the user's setting for VT is enabled for the subscription.
+     */
+    boolean isVtSettingEnabled(int subId, String callingPackage);
+
+    /**
+     * Modify the user's setting for whether or not VT is available for the subscrption specified.
+     */
+    void setVtSetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if the user's setting for whether or not Voice over WiFi is currently enabled.
+     */
+    boolean isVoWiFiSettingEnabled(int subId);
+
+    /**
+     * sets the user's setting for Voice over WiFi enabled state.
+     */
+    void setVoWiFiSetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if the user's setting for Voice over WiFi while roaming is enabled.
+     */
+    boolean isVoWiFiRoamingSettingEnabled(int subId);
+
+    /**
+     * Sets the user's preference for whether or not Voice over WiFi is enabled for the current
+     * subscription while roaming.
+     */
+    void setVoWiFiRoamingSetting(int subId, boolean isEnabled);
+
+    /**
+     * Set the Voice over WiFi enabled state, but do not persist the setting.
+     */
+    void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode);
+
+    /**
+     * return the Voice over WiFi mode preference set by the user for the subscription specified.
+     */
+    int getVoWiFiModeSetting(int subId);
+
+    /**
+     * sets the user's preference for the Voice over WiFi mode for the subscription specified.
+     */
+    void setVoWiFiModeSetting(int subId, int mode);
+
+    /**
+     * return the Voice over WiFi mode preference set by the user for the subscription specified
+     * while roaming.
+     */
+    int getVoWiFiRoamingModeSetting(int subId);
+
+    /**
+     * sets the user's preference for the Voice over WiFi mode for the subscription specified
+     * while roaming.
+     */
+    void setVoWiFiRoamingModeSetting(int subId, int mode);
+
+    /**
+     * Modify the user's setting for whether or not RTT is enabled for the subscrption specified.
+     */
+    void setRttCapabilitySetting(int subId, boolean isEnabled);
+
+    /**
+     * return true if TTY over VoLTE is enabled for the subscription specified.
+     */
+    boolean isTtyOverVolteEnabled(int subId);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index c03065c..0baf860 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -80,4 +80,5 @@
     void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
     void notifyPreferredDataSubIdChanged(int preferredSubId);
+    void notifyRadioPowerStateChanged(in int state);
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 9730ebc..eda8e77 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -44,10 +45,6 @@
 
     private static final boolean DBG = false;
 
-    // When set to true this flag will treat all apps that fail the device identifier check as
-    // though they are targeting pre-Q and return dummy data instead of throwing a SecurityException
-    private static final boolean RELAX_DEVICE_IDENTIFIER_CHECK = true;
-
     private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () ->
             ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
 
@@ -280,23 +277,29 @@
      */
     private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
             int uid, String callingPackage, String message) {
-        // if the device identifier check is relaxed then just return false to return dummy data to
-        // the caller instead of throwing a SecurityException for apps targeting Q+.
-        if (RELAX_DEVICE_IDENTIFIER_CHECK) {
-            Log.wtf(LOG_TAG,
-                    "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
-            return false;
+        Log.wtf(LOG_TAG,
+                "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
+        // if the device identifier check is relaxed then revert to the READ_PHONE_STATE permission
+        // check that was previously required to access device identifiers.
+        boolean relaxDeviceIdentifierCheck = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_CHECK_ENABLED, 0) == 0;
+        if (relaxDeviceIdentifierCheck) {
+            return checkReadPhoneState(context, subId, pid, uid, callingPackage, message);
         } else {
+            boolean targetQBehaviorDisabled = Settings.Global.getInt(context.getContentResolver(),
+                    Settings.Global.PRIVILEGED_DEVICE_IDENTIFIER_TARGET_Q_BEHAVIOR_ENABLED, 0) == 0;
             if (callingPackage != null) {
                 try {
-                    // if the target SDK is pre-Q then check if the calling package would have
-                    // previously had access to device identifiers.
+                    // if the target SDK is pre-Q or the target Q behavior is disabled then check if
+                    // the calling package would have previously had access to device identifiers.
                     ApplicationInfo callingPackageInfo =
                             context.getPackageManager().getApplicationInfo(
                                     callingPackage, 0);
-                    if (callingPackageInfo != null
-                            && callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
-                        if (context.checkPermission(android.Manifest.permission.READ_PHONE_STATE,
+                    if (callingPackageInfo != null && (
+                            callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q
+                                    || targetQBehaviorDisabled)) {
+                        if (context.checkPermission(
+                                android.Manifest.permission.READ_PHONE_STATE,
                                 pid,
                                 uid) == PackageManager.PERMISSION_GRANTED) {
                             return false;
@@ -312,8 +315,8 @@
                     // default to throwing the SecurityException.
                 }
             }
-            throw new SecurityException(message + ": The user " + uid + " does not have the "
-                    + "READ_PRIVILEGED_PHONE_STATE permission to access the device identifiers");
+            throw new SecurityException(message + ": The user " + uid
+                    + " does not meet the requirements to access device identifiers.");
         }
     }
 
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index 3bd3d68..2b968ae 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -29,6 +29,7 @@
     method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
+    method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
     method public void setUpdateAvailable(java.lang.String, boolean);
     method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 9d260eb..fa5b896 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -774,6 +774,12 @@
 
     /** @hide */
     @Override
+    public int getDisplayId() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
     public void updateDisplay(int displayId) {
         throw new UnsupportedOperationException();
     }
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 976848c..eed8ae7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -92,7 +92,9 @@
             "com.google.android.wearable.action.GOOGLE";
     private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
     private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
-    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
+    private static final int BEFORE_FORCE_STOP_SLEEP_TIMEOUT = 1000; // 1s before force stopping
+    private static final int BEFORE_KILL_APP_SLEEP_TIMEOUT = 1000; // 1s before killing
+    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 3000; // 3s between launching apps
     private static final int PROFILE_SAVE_SLEEP_TIMEOUT = 1000; // Allow 1s for the profile to save
     private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
     private static final String LAUNCH_FILE = "applaunch.txt";
@@ -327,7 +329,14 @@
                     }
                 }
                 if(mForceStopApp) {
-                    closeApp(launch.getApp());
+                    sleep(BEFORE_FORCE_STOP_SLEEP_TIMEOUT);
+                    forceStopApp(launch.getApp());
+                    sleep(BEFORE_KILL_APP_SLEEP_TIMEOUT);
+                    // Close again for good measure (just in case).
+                    forceStopApp(launch.getApp());
+                    // Kill the backgrounded process in the case forceStopApp only sent it to
+                    // background.
+                    killBackgroundApp(launch.getApp());
                 } else {
                     startHomeIntent();
                 }
@@ -638,7 +647,7 @@
         // Kill all the apps
         for (String appName : mNameToIntent.keySet()) {
             Log.w(TAG, String.format("killing %s", appName));
-            closeApp(appName);
+            forceStopApp(appName);
         }
         // Drop all the cache.
         assertNotNull("Issue in dropping the cache",
@@ -646,7 +655,7 @@
                         .executeShellCommand(DROP_CACHE_SCRIPT));
     }
 
-    private void closeApp(String appName) {
+    private void forceStopApp(String appName) {
         Intent startIntent = mNameToIntent.get(appName);
         if (startIntent != null) {
             String packageName = startIntent.getComponent().getPackageName();
@@ -658,6 +667,18 @@
         }
     }
 
+    private void killBackgroundApp(String appName) {
+        Intent startIntent = mNameToIntent.get(appName);
+        if (startIntent != null) {
+            String packageName = startIntent.getComponent().getPackageName();
+            try {
+                mAm.killBackgroundProcesses(packageName, UserHandle.USER_CURRENT);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error closing app", e);
+            }
+        }
+    }
+
     private void sleep(int time) {
         try {
             Thread.sleep(time);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
index 5bc8934..571f623 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -22,9 +22,9 @@
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
 import android.graphics.Paint.Style;
+import android.graphics.RecordingCanvas;
 import android.os.Bundle;
 import android.os.Trace;
-import android.view.DisplayListCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -88,8 +88,8 @@
             super.onDraw(canvas);
 
             if (canvas.isHardwareAccelerated()) {
-                DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
-                displayListCanvas.drawCircle(mX, mY, mRadius, mPaint);
+                RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
+                recordingCanvas.drawCircle(mX, mY, mRadius, mPaint);
             }
         }
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
index af8e10b..220016a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
@@ -16,25 +16,13 @@
 
 package com.android.test.hwui;
 
-import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
-import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
-import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
-import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_RARELY;
-
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.GraphicBuffer;
 import android.graphics.Paint;
 import android.graphics.Picture;
-import android.graphics.PixelFormat;
-import android.graphics.SurfaceTexture;
 import android.os.Bundle;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-import android.view.Surface;
-import android.view.ThreadedRenderer;
 import android.widget.ImageView;
 
 public class DrawIntoHwBitmapActivity extends Activity {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
index 7713f5d..e7d7f2b 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -21,12 +21,12 @@
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.view.DisplayListCanvas;
 import android.view.ThreadedRenderer;
-import android.view.RenderNode;
+import android.graphics.RenderNode;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.AbsoluteLayout;
@@ -206,7 +206,7 @@
                     }
 
                     // Draw frame
-                    DisplayListCanvas canvas = nodeFrame.start(currentFrameBounds.width(),
+                    RecordingCanvas canvas = nodeFrame.start(currentFrameBounds.width(),
                             currentFrameBounds.height());
                     mFrameContent.draw(canvas);
                     nodeFrame.end(canvas);
@@ -228,7 +228,7 @@
                     }
 
                     // Draw Backdrop
-                    DisplayListCanvas canvas = nodeBack.start(currentBackBounds.width(),
+                    RecordingCanvas canvas = nodeBack.start(currentBackBounds.width(),
                             currentBackBounds.height());
                     mBackContent.draw(canvas);
                     nodeBack.end(canvas);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java
index be5d7f9..4eb4072 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java
@@ -8,9 +8,8 @@
 
 import android.app.Activity;
 import android.util.AttributeSet;
-import android.view.RenderNode;
+import android.graphics.RenderNode;
 import android.view.View;
-import android.widget.LinearLayout;
 
 public class ProjectionActivity extends Activity {
     /**
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java
index 2ae960b..9abd7ea 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java
@@ -1,13 +1,7 @@
 package com.android.test.hwui;
 
 import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
 import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.RenderNode;
 import android.view.View;
 
 public class ProjectionClippingActivity extends Activity {
diff --git a/tests/Internal/res/xml/livewallpaper.xml b/tests/Internal/res/xml/livewallpaper.xml
index 6b5e84e..36e7e41 100644
--- a/tests/Internal/res/xml/livewallpaper.xml
+++ b/tests/Internal/res/xml/livewallpaper.xml
@@ -16,4 +16,5 @@
   -->
 <wallpaper
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:supportsAmbientMode="true"/>
\ No newline at end of file
+    android:settingsSliceUri="content://com.android.internal.tests/slice"
+    android:supportsAmbientMode="true"/>
diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java
index 98045ae..7f06f2c 100644
--- a/tests/Internal/src/android/app/WallpaperInfoTest.java
+++ b/tests/Internal/src/android/app/WallpaperInfoTest.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.Parcel;
 import android.service.wallpaper.WallpaperService;
 import android.support.test.InstrumentationRegistry;
@@ -64,5 +65,31 @@
                 fromParcel.supportsAmbientMode());
         parcel.recycle();
     }
+
+    @Test
+    public void testGetSettingsSliceUri() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+
+        Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+        intent.setPackage("com.android.internal.tests");
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> result = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+        assertEquals(1, result.size());
+        ResolveInfo info = result.get(0);
+        WallpaperInfo wallpaperInfo = new WallpaperInfo(context, info);
+
+        // This expected Uri must be the same as that in livewallpaper.xml
+        Uri expectedUri = Uri.parse("content://com.android.internal.tests/slice");
+        Uri settingsUri = wallpaperInfo.getSettingsSliceUri();
+        assertEquals("The loaded URI should equal to the string in livewallpaper.xml",
+                0, expectedUri.compareTo(settingsUri));
+        Parcel parcel = Parcel.obtain();
+        wallpaperInfo.writeToParcel(parcel, 0 /* flags */);
+        parcel.setDataPosition(0);
+        WallpaperInfo fromParcel = WallpaperInfo.CREATOR.createFromParcel(parcel);
+        assertEquals("settingsSliceUri should be restorable from parcelable",
+                0, expectedUri.compareTo(fromParcel.getSettingsSliceUri()));
+        parcel.recycle();
+    }
 }
 
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
index 6500428..18cdf96 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -34,7 +34,7 @@
         // Do an explicit GC in the system server process as part of the test
         // case to reduce GC-related sources of noise.
         // SIGUSR1 = 10 is the magic signal to trigger the GC.
-        int pid = mDevice.getPidForProcess("system_server");
+        int pid = mDevice.getProcessPid("system_server");
         mDevice.executeShellCommand("kill -10 " + pid);
 
         // Invoke the Device Cujs instrumentation to run the cujs.
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
index 03503ce..26146ca 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Device.java
@@ -19,9 +19,6 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 
-import java.util.InputMismatchException;
-import java.util.Scanner;
-
 /**
  * Wrapper around ITestDevice exposing useful device functions.
  */
@@ -58,29 +55,15 @@
     /**
      * Returns the pid for the process with the given name.
      */
-    public int getPidForProcess(String name) throws TestException {
-        String psout = executeShellCommand("ps -A -o PID,CMD");
-        Scanner sc = new Scanner(psout);
+    public int getProcessPid(String name) throws TestException {
         try {
-            // ps output is of the form:
-            //  PID CMD
-            //    1 init
-            //    2 kthreadd
-            //    ...
-            // 9693 ps
-            sc.nextLine();
-            while (sc.hasNextLine()) {
-                int pid = sc.nextInt();
-                String cmd = sc.next();
-
-                if (name.equals(cmd)) {
-                    return pid;
-                }
+            String pid = mDevice.getProcessPid(name);
+            if (pid == null) {
+                throw new TestException("failed to get pid for " + name);
             }
-        } catch (InputMismatchException e) {
-            throw new TestException("unexpected ps output format: " + psout, e);
+            return Integer.parseInt(pid);
+        } catch (DeviceNotAvailableException e) {
+            throw new TestException(e);
         }
-
-        throw new TestException("failed to get pid for process " + name);
     }
 }
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
index b408a81..b46e642 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -79,7 +79,7 @@
         // adb root access is required to get showmap
         mDevice.enableAdbRoot();
 
-        int pid = mDevice.getPidForProcess("system_server");
+        int pid = mDevice.getProcessPid("system_server");
 
         // Read showmap for system server and add it as a test log
         String showmap = mDevice.executeShellCommand("showmap " + pid);
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 03a617c..6174c6c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -219,7 +219,7 @@
         // callback triggers
         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
         verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
-                any(NetworkCapabilities.class), any(LinkProperties.class));
+                any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
@@ -247,7 +247,7 @@
         // callback triggers
         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
         verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
-                any(NetworkCapabilities.class), any(LinkProperties.class));
+                any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c77fcc..17bcea0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,6 +51,10 @@
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
 import static com.android.internal.util.TestUtils.waitForIdleHandler;
 import static com.android.internal.util.TestUtils.waitForIdleLooper;
@@ -92,6 +96,7 @@
 import android.net.ConnectivityManager.PacketKeepaliveCallback;
 import android.net.ConnectivityManager.TooManyRequestsException;
 import android.net.ConnectivityThread;
+import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
@@ -148,6 +153,7 @@
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.NetworkPinner;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -215,11 +221,13 @@
     private MockNetworkAgent mEthernetNetworkAgent;
     private MockVpn mMockVpn;
     private Context mContext;
+    private INetworkPolicyListener mPolicyListener;
 
     @Mock IpConnectivityMetrics.Logger mMetricsService;
     @Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
+    @Mock INetworkPolicyManager mNpm;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -934,6 +942,11 @@
         }
 
         @Override
+        protected Tethering makeTethering() {
+            return mock(Tethering.class);
+        }
+
+        @Override
         protected int reserveNetId() {
             while (true) {
                 final int netId = super.reserveNetId();
@@ -1023,6 +1036,20 @@
         public void waitForIdle() {
             waitForIdle(TIMEOUT_MS);
         }
+
+        public void setUidRulesChanged(int uidRules) {
+            try {
+                mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+            } catch (RemoteException ignored) {
+            }
+        }
+
+        public void setRestrictBackgroundChanged(boolean restrictBackground) {
+            try {
+                mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+            } catch (RemoteException ignored) {
+            }
+        }
     }
 
     /**
@@ -1055,12 +1082,18 @@
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(
                 NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
+
         mService = new WrappedConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
-                mock(INetworkPolicyManager.class),
+                mNpm,
                 mock(IpConnectivityLog.class));
 
+        final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
+                ArgumentCaptor.forClass(INetworkPolicyListener.class);
+        verify(mNpm).registerListener(policyListenerCaptor.capture());
+        mPolicyListener = policyListenerCaptor.getValue();
+
         // Create local CM before sending system ready so that we can answer
         // getSystemService() correctly.
         mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
@@ -1441,7 +1474,8 @@
         RESUMED,
         LOSING,
         LOST,
-        UNAVAILABLE
+        UNAVAILABLE,
+        BLOCKED_STATUS
     }
 
     private static class CallbackInfo {
@@ -1522,6 +1556,11 @@
             setLastCallback(CallbackState.LOST, network, null);
         }
 
+        @Override
+        public void onBlockedStatusChanged(Network network, boolean blocked) {
+            setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+        }
+
         public Network getLastAvailableNetwork() {
             return mLastAvailableNetwork;
         }
@@ -1582,6 +1621,7 @@
         // - onSuspended, iff the network was suspended when the callbacks fire.
         // - onCapabilitiesChanged.
         // - onLinkPropertiesChanged.
+        // - onBlockedStatusChanged.
         //
         // @param agent the network to expect the callbacks on.
         // @param expectSuspended whether to expect a SUSPENDED callback.
@@ -1589,7 +1629,7 @@
         //        onCapabilitiesChanged callback.
         // @param timeoutMs how long to wait for the callbacks.
         void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
-                boolean expectValidated, int timeoutMs) {
+                boolean expectValidated, boolean expectBlocked, int timeoutMs) {
             expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
             if (expectSuspended) {
                 expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1600,19 +1640,28 @@
                 expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
             }
             expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+            expectBlockedStatusCallback(expectBlocked, agent);
         }
 
         // Expects the available callbacks (validated), plus onSuspended.
         void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
-            expectAvailableCallbacks(agent, true, expectValidated, TEST_CALLBACK_TIMEOUT_MS);
+            expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, true, TEST_CALLBACK_TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
-            expectAvailableCallbacks(agent, false, false, TEST_CALLBACK_TIMEOUT_MS);
+            expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
+        }
+
+        void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+            expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
         }
 
         // Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1623,6 +1672,9 @@
             expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
             NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
             expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
+            // Implicitly check the network is allowed to use.
+            // TODO: should we need to consider if network is in blocked status in this case?
+            expectBlockedStatusCallback(false, agent);
             NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
             assertEquals(nc1, nc2);
         }
@@ -1665,6 +1717,12 @@
                     fn.test((NetworkCapabilities) cbi.arg));
         }
 
+        void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+            CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
+            boolean actualBlocked = (boolean) cbi.arg;
+            assertEquals(expectBlocked, actualBlocked);
+        }
+
         void assertNoCallback() {
             waitForIdle();
             CallbackInfo c = mCallbacks.peek();
@@ -3223,7 +3281,7 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
 
         // pass timeout and validate that UNAVAILABLE is not called
@@ -3243,7 +3301,7 @@
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+        networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
                 TEST_CALLBACK_TIMEOUT_MS);
         mWiFiNetworkAgent.disconnect();
         networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3802,6 +3860,7 @@
         networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
         CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
                 networkAgent);
+        networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
         networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
         networkCallback.assertNoCallback();
         checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
@@ -4010,6 +4069,7 @@
                 mCellNetworkAgent);
         CallbackInfo cbi = cellNetworkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4068,6 +4128,7 @@
                 mCellNetworkAgent);
         CallbackInfo cbi = cellNetworkCallback.expectCallback(
                 CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
         assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
         assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4444,6 +4505,101 @@
         mMockVpn.disconnect();
     }
 
+    @Test
+    public void testNetworkBlockedStatus() {
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR)
+                .build();
+        mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+        // ConnectivityService should cache it not to invoke the callback again.
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        cellNetworkCallback.assertNoCallback();
+
+        mService.setUidRulesChanged(RULE_NONE);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+        // Restrict the network based on UID rule and NOT_METERED capability change.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+        cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+                mCellNetworkAgent);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        mService.setUidRulesChanged(RULE_ALLOW_METERED);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        mService.setUidRulesChanged(RULE_NONE);
+        cellNetworkCallback.assertNoCallback();
+
+        // Restrict the network based on BackgroundRestricted.
+        mService.setRestrictBackgroundChanged(true);
+        cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+        mService.setRestrictBackgroundChanged(true);
+        cellNetworkCallback.assertNoCallback();
+        mService.setRestrictBackgroundChanged(false);
+        cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(cellNetworkCallback);
+    }
+
+    @Test
+    public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        // No Networkcallbacks invoked before any network is active.
+        mService.setUidRulesChanged(RULE_REJECT_ALL);
+        mService.setUidRulesChanged(RULE_NONE);
+        mService.setUidRulesChanged(RULE_REJECT_METERED);
+        defaultCallback.assertNoCallback();
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+        defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+
+        // Allow to use the network after switching to NOT_METERED network.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+        defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+        // Switch to METERED network. Restrict the use of the network.
+        mWiFiNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+        defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
+
+        // Network becomes NOT_METERED.
+        mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+        defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+        // Verify there's no Networkcallbacks invoked after data saver on/off.
+        mService.setRestrictBackgroundChanged(true);
+        mService.setRestrictBackgroundChanged(false);
+        defaultCallback.assertNoCallback();
+
+        mCellNetworkAgent.disconnect();
+        defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        defaultCallback.assertNoCallback();
+
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
     /**
      * Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
      */
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index d631f35..f06643d 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -28,7 +28,7 @@
 
 /* these formats are already compressed, or don't compress well */
 static const char* kNoCompressExt[] = {
-    ".jpg", ".jpeg", ".png", ".gif",
+    ".jpg", ".jpeg", ".png", ".gif", ".opus",
     ".wav", ".mp2", ".mp3", ".ogg", ".aac",
     ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
     ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index c9987b8..b165c6b 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -2,10 +2,19 @@
 
 include $(CLEAR_VARS)
 
+aapt2_results := $(call intermediates-dir-for,PACKAGING,aapt2_run_host_unit_tests)/result.xml
+
 # Target for running host unit tests on post/pre-submit.
 .PHONY: aapt2_run_host_unit_tests
-aapt2_run_host_unit_tests: PRIVATE_GTEST_OPTIONS := --gtest_output=xml:$(DIST_DIR)/gtest/aapt2_host_unit_tests_result.xml
-aapt2_run_host_unit_tests: $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
-	-$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests $(PRIVATE_GTEST_OPTIONS) > /dev/null 2>&1
+aapt2_run_host_unit_tests: $(aapt2_results)
+
+$(call dist-for-goals,aapt2_run_host_unit_tests,$(aapt2_results):gtest/aapt2_host_unit_tests_result.xml)
+
+# Always run the tests again, even if they haven't changed
+$(aapt2_results): .KATI_IMPLICIT_OUTPUTS := $(aapt2_results)-nocache
+$(aapt2_results): $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
+	-$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests --gtest_output=xml:$@ > /dev/null 2>&1
+
+aapt2_results :=
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index a20b9b7..b353ff0 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -35,6 +35,43 @@
 
 namespace aapt {
 
+static ApkFormat DetermineApkFormat(io::IFileCollection* apk) {
+  if (apk->FindFile(kApkResourceTablePath) != nullptr) {
+    return ApkFormat::kBinary;
+  } else if (apk->FindFile(kProtoResourceTablePath) != nullptr) {
+    return ApkFormat::kProto;
+  } else {
+    // If the resource table is not present, attempt to read the manifest.
+    io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
+    if (manifest_file == nullptr) {
+      return ApkFormat::kUnknown;
+    }
+
+    // First try in proto format.
+    std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
+    if (manifest_in != nullptr) {
+      pb::XmlNode pb_node;
+      io::ProtoInputStreamReader proto_reader(manifest_in.get());
+      if (proto_reader.ReadMessage(&pb_node)) {
+        return ApkFormat::kProto;
+      }
+    }
+
+    // If it didn't work, try in binary format.
+    std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
+    if (manifest_data != nullptr) {
+      std::string error;
+      std::unique_ptr<xml::XmlResource> manifest =
+          xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
+      if (manifest != nullptr) {
+        return ApkFormat::kBinary;
+      }
+    }
+
+    return ApkFormat::kUnknown;
+  }
+}
+
 std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) {
   Source source(path);
   std::string error;
@@ -301,41 +338,4 @@
   return doc;
 }
 
-ApkFormat LoadedApk::DetermineApkFormat(io::IFileCollection* apk) {
-  if (apk->FindFile(kApkResourceTablePath) != nullptr) {
-    return ApkFormat::kBinary;
-  } else if (apk->FindFile(kProtoResourceTablePath) != nullptr) {
-    return ApkFormat::kProto;
-  } else {
-    // If the resource table is not present, attempt to read the manifest.
-    io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
-    if (manifest_file == nullptr) {
-      return ApkFormat::kUnknown;
-    }
-
-    // First try in proto format.
-    std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
-    if (manifest_in != nullptr) {
-      pb::XmlNode pb_node;
-      io::ProtoInputStreamReader proto_reader(manifest_in.get());
-      if (!proto_reader.ReadMessage(&pb_node)) {
-        return ApkFormat::kProto;
-      }
-    }
-
-    // If it didn't work, try in binary format.
-    std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
-    if (manifest_data != nullptr) {
-      std::string error;
-      std::unique_ptr<xml::XmlResource> manifest =
-          xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
-      if (manifest != nullptr) {
-        return ApkFormat::kBinary;
-      }
-    }
-
-    return ApkFormat::kUnknown;
-  }
-}
-
 }  // namespace aapt
diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h
index 84c57c1..5b6f45e 100644
--- a/tools/aapt2/LoadedApk.h
+++ b/tools/aapt2/LoadedApk.h
@@ -121,8 +121,6 @@
   std::unique_ptr<ResourceTable> table_;
   std::unique_ptr<xml::XmlResource> manifest_;
   ApkFormat format_;
-
-  static ApkFormat DetermineApkFormat(io::IFileCollection* apk);
 };
 
 }  // namespace aapt
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
new file mode 100755
index 0000000..48c0755
--- /dev/null
+++ b/tools/hiddenapi/merge_csv.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Merge mutliple CSV files, possibly with different columns, writing to stdout.
+"""
+
+import csv
+import sys
+
+csv_readers = [
+    csv.DictReader(open(csv_file, 'rb'), delimiter=',', quotechar='|')
+    for csv_file in sys.argv[1:]
+]
+
+# Build union of all columns from source files:
+headers = set()
+for reader in csv_readers:
+  headers = headers.union(reader.fieldnames)
+
+# Concatenate all files to output:
+out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', fieldnames = sorted(headers))
+out.writeheader()
+for reader in csv_readers:
+  for row in reader:
+    out.writerow(row)
+
+
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
index 76a2f2d..710da40 100755
--- a/tools/hiddenapi/sort_api.sh
+++ b/tools/hiddenapi/sort_api.sh
@@ -21,4 +21,6 @@
 A=( ${C[*]} ${A[*]} )
 unset IFS
 # Dump array back into the file
-printf '%s\n' "${A[@]}" > "$dest_list"
+if [ ${#A[@]} -ne 0 ]; then
+  printf '%s\n' "${A[@]}" > "$dest_list"
+fi
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index d1f42f8..257043b 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -47,7 +47,8 @@
       fields(that.fields),
       primaryFields(that.primaryFields),
       exclusiveField(that.exclusiveField),
-      uidField(that.uidField) {}
+      uidField(that.uidField),
+      binaryFields(that.binaryFields) {}
 
 AtomDecl::AtomDecl(int c, const string& n, const string& m)
     :code(c),
@@ -119,6 +120,9 @@
             } else if (field->message_type()->full_name() ==
                        "android.os.statsd.KeyValuePair") {
               return JAVA_TYPE_KEY_VALUE_PAIR;
+            } else if (field->options().GetExtension(os::statsd::log_mode) ==
+                       os::statsd::LogMode::MODE_BYTES) {
+                return JAVA_TYPE_BYTE_ARRAY;
             } else {
                 return JAVA_TYPE_OBJECT;
             }
@@ -188,6 +192,8 @@
   for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
        it != fields.end(); it++) {
     const FieldDescriptor *field = it->second;
+    bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+                         os::statsd::LogMode::MODE_BYTES;
 
     java_type_t javaType = java_type(field);
 
@@ -197,17 +203,24 @@
       continue;
     } else if (javaType == JAVA_TYPE_OBJECT &&
                atomDecl->code < PULL_ATOM_START_ID) {
-      // Allow attribution chain, but only at position 1.
-      print_error(field,
-                  "Message type not allowed for field in pushed atoms: %s\n",
-                  field->name().c_str());
-      errorCount++;
-      continue;
-    } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
-      print_error(field, "Raw bytes type not allowed for field: %s\n",
-                  field->name().c_str());
-      errorCount++;
-      continue;
+        // Allow attribution chain, but only at position 1.
+        print_error(field,
+                    "Message type not allowed for field in pushed atoms: %s\n",
+                    field->name().c_str());
+        errorCount++;
+        continue;
+    } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
+        print_error(field, "Raw bytes type not allowed for field: %s\n",
+                    field->name().c_str());
+        errorCount++;
+        continue;
+    }
+
+    if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
+        print_error(field, "Cannot mark field %s as bytes.\n",
+                    field->name().c_str());
+        errorCount++;
+        continue;
     }
   }
 
@@ -233,6 +246,8 @@
        it != fields.end(); it++) {
     const FieldDescriptor *field = it->second;
     java_type_t javaType = java_type(field);
+    bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+                         os::statsd::LogMode::MODE_BYTES;
 
     AtomField atField(field->name(), javaType);
     // Generate signature for pushed atoms
@@ -241,8 +256,10 @@
         // All enums are treated as ints when it comes to function signatures.
         signature->push_back(JAVA_TYPE_INT);
         collate_enums(*field->enum_type(), &atField);
+      } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+          signature->push_back(JAVA_TYPE_BYTE_ARRAY);
       } else {
-        signature->push_back(javaType);
+          signature->push_back(javaType);
       }
     }
     if (javaType == JAVA_TYPE_ENUM) {
@@ -287,6 +304,10 @@
             errorCount++;
         }
     }
+    // Binary field validity is already checked above.
+    if (isBinaryField) {
+        atomDecl->binaryFields.push_back(it->first);
+    }
   }
 
   return errorCount;
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 31b8b07..450b305 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -89,6 +89,8 @@
 
     int uidField = 0;
 
+    vector<int> binaryFields;
+
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 56c8428..1ef34b9 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -66,6 +66,8 @@
             return "double";
         case JAVA_TYPE_STRING:
             return "char const*";
+        case JAVA_TYPE_BYTE_ARRAY:
+            return "char const*";
         default:
             return "UNKNOWN";
     }
@@ -88,6 +90,8 @@
             return "double";
         case JAVA_TYPE_STRING:
             return "java.lang.String";
+        case JAVA_TYPE_BYTE_ARRAY:
+            return "byte[]";
         default:
             return "UNKNOWN";
     }
@@ -198,13 +202,40 @@
     }
 
     fprintf(out, "    return options;\n");
-    fprintf(out, "  }\n");
+    fprintf(out, "}\n");
 
     fprintf(out,
             "const std::map<int, StateAtomFieldOptions> "
             "AtomsInfo::kStateAtomsFieldOptions = "
             "getStateAtomFieldOptions();\n");
 
+    fprintf(out,
+            "static std::map<int, std::vector<int>> "
+            "getBinaryFieldAtoms() {\n");
+    fprintf(out, "    std::map<int, std::vector<int>> options;\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (atom->binaryFields.size() == 0) {
+            continue;
+        }
+        fprintf(out,
+                "\n    // Adding binary fields for atom "
+                "(%d)%s\n",
+                atom->code, atom->name.c_str());
+
+        for (const auto& field : atom->binaryFields) {
+            fprintf(out, "    options[static_cast<int>(%s)].push_back(%d);\n",
+                    make_constant_name(atom->name).c_str(), field);
+        }
+    }
+
+    fprintf(out, "    return options;\n");
+    fprintf(out, "}\n");
+
+    fprintf(out,
+            "const std::map<int, std::vector<int>> "
+            "AtomsInfo::kBytesFieldAtoms = "
+            "getBinaryFieldAtoms();\n");
 
     fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
     fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
@@ -664,6 +695,9 @@
     fprintf(out,
             "  const static std::map<int, StateAtomFieldOptions> "
             "kStateAtomsFieldOptions;\n");
+    fprintf(out,
+            "  const static std::map<int, std::vector<int>> "
+            "kBytesFieldAtoms;");
     fprintf(out, "};\n");
 
     fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
@@ -698,6 +732,8 @@
             fprintf(out, ", android.os.WorkSource workSource");
         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
             fprintf(out, ", SparseArray<Object> value_map");
+        } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
+            fprintf(out, ", byte[] %s", field->name.c_str());
         } else {
             fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
         }
@@ -890,6 +926,8 @@
             return "jdouble";
         case JAVA_TYPE_STRING:
             return "jstring";
+        case JAVA_TYPE_BYTE_ARRAY:
+            return "jbyteArray";
         default:
             return "UNKNOWN";
     }
@@ -942,6 +980,9 @@
             case JAVA_TYPE_KEY_VALUE_PAIR:
               result += "_KeyValuePairs";
               break;
+            case JAVA_TYPE_BYTE_ARRAY:
+                result += "_bytes";
+                break;
             default:
                 result += "_UNKNOWN";
                 break;
@@ -967,6 +1008,8 @@
             return "D";
         case JAVA_TYPE_STRING:
             return "Ljava/lang/String;";
+        case JAVA_TYPE_BYTE_ARRAY:
+            return "[B";
         default:
             return "UNKNOWN";
     }
@@ -1081,6 +1124,25 @@
                 fprintf(out, "    } else {\n");
                 fprintf(out, "        str%d = NULL;\n", argIndex);
                 fprintf(out, "    }\n");
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                hadStringOrChain = true;
+                fprintf(out, "    jbyte* jbyte_array%d;\n", argIndex);
+                fprintf(out, "    const char* str%d;\n", argIndex);
+                fprintf(out, "    if (arg%d != NULL) {\n", argIndex);
+                fprintf(out,
+                        "        jbyte_array%d = "
+                        "env->GetByteArrayElements(arg%d, NULL);\n",
+                        argIndex, argIndex);
+                fprintf(out,
+                        "        str%d = "
+                        "reinterpret_cast<char*>(env->GetByteArrayElements(arg%"
+                        "d, NULL));\n",
+                        argIndex, argIndex);
+                fprintf(out, "    } else {\n");
+                fprintf(out, "        jbyte_array%d = NULL;\n", argIndex);
+                fprintf(out, "        str%d = NULL;\n", argIndex);
+                fprintf(out, "    }\n");
+
             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 hadStringOrChain = true;
                 for (auto chainField : attributionDecl.fields) {
@@ -1154,7 +1216,10 @@
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", int32_t_map, int64_t_map, string_map, float_map");
             } else {
-                const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+                const char* argName = (*arg == JAVA_TYPE_STRING ||
+                                       *arg == JAVA_TYPE_BYTE_ARRAY)
+                                              ? "str"
+                                              : "arg";
                 fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
             }
             argIndex++;
@@ -1171,6 +1236,13 @@
                 fprintf(out, "        env->ReleaseStringUTFChars(arg%d, str%d);\n",
                         argIndex, argIndex);
                 fprintf(out, "    }\n");
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out, "    if (str%d != NULL) { \n", argIndex);
+                fprintf(out,
+                        "        env->ReleaseByteArrayElements(arg%d, "
+                        "jbyte_array%d, 0);\n",
+                        argIndex, argIndex);
+                fprintf(out, "    }\n");
             } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 for (auto chainField : attributionDecl.fields) {
                     if (chainField.javaType == JAVA_TYPE_INT) {
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index f635974..3be87d9 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -109,6 +109,28 @@
   oneof event { BadAttributionNodePositionAtom bad = 1; }
 }
 
+message GoodEventWithBinaryFieldAtom {
+    oneof event { GoodBinaryFieldAtom field1 = 1; }
+}
+
+message ComplexField {
+    optional string str = 1;
+}
+
+message GoodBinaryFieldAtom {
+    optional int32 field1 = 1;
+    optional ComplexField bf = 2 [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+message BadEventWithBinaryFieldAtom {
+    oneof event { BadBinaryFieldAtom field1 = 1; }
+}
+
+message BadBinaryFieldAtom {
+    optional int32 field1 = 1;
+    optional ComplexField bf = 2;
+}
+
 message BadStateAtoms {
     oneof event {
         BadStateAtom1 bad1 = 1;
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 1936d96..ad3bffac 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -212,5 +212,19 @@
     EXPECT_EQ(0, errorCount);
 }
 
+TEST(CollationTest, PassOnGoodBinaryFieldAtom) {
+    Atoms atoms;
+    int errorCount =
+            collate_atoms(GoodEventWithBinaryFieldAtom::descriptor(), &atoms);
+    EXPECT_EQ(0, errorCount);
+}
+
+TEST(CollationTest, FailOnBadBinaryFieldAtom) {
+    Atoms atoms;
+    int errorCount =
+            collate_atoms(BadEventWithBinaryFieldAtom::descriptor(), &atoms);
+    EXPECT_TRUE(errorCount > 0);
+}
+
 }  // namespace stats_log_api_gen
 }  // namespace android
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 59ba8e7..9adbe67 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3754,6 +3754,13 @@
                 mCallback.onProvisioningFailure(status);
             });
         }
+
+        @Override
+        public void onProvisioningComplete() {
+            mHandler.post(() -> {
+                mCallback.onProvisioningComplete();
+            });
+        }
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl b/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl
index c2cb16a..a6bdd5b6 100644
--- a/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl
+++ b/wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl
@@ -32,5 +32,10 @@
      * Service to manager callback providing Provisioning status
      */
     void onProvisioningStatus(int status);
+
+    /**
+     * Service to manager callback providing completion of Provisioning/Remediation flow
+     */
+    void onProvisioningComplete();
 }
 
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 6076175..4b76526 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -108,6 +108,48 @@
     public static final int OSU_FAILURE_UNEXPECTED_SOAP_MESSAGE_STATUS = 15;
 
     /**
+     * The reason code for provisioning failure when there is no PPS MO.
+     * MO.
+     */
+    public static final int OSU_FAILURE_NO_PPS_MO = 16;
+
+    /**
+     * The reason code for provisioning failure when there is no AAAServerTrustRoot node in a PPS
+     * MO.
+     */
+    public static final int OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17;
+
+    /**
+     * The reason code for provisioning failure when there is no TrustRoot node for remediation
+     * server in a PPS MO.
+     */
+    public static final int OSU_FAILURE_NO_REMEDIATION_SERVER_TRUST_ROOT_NODE = 18;
+
+    /**
+     * The reason code for provisioning failure when there is no TrustRoot node for policy server in
+     * a PPS MO.
+     */
+    public static final int OSU_FAILURE_NO_POLICY_SERVER_TRUST_ROOT_NODE = 19;
+
+    /**
+     * The reason code for provisioning failure when failing to retrieve trust root certificates
+     * used for validating server certificate for AAA, Remediation and Policy server.
+     */
+    public static final int OSU_FAILURE_RETRIEVE_TRUST_ROOT_CERTIFICATES = 20;
+
+    /**
+     * The reason code for provisioning failure when there is no trust root certificate for AAA
+     * server.
+     */
+    public static final int OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21;
+
+    /**
+     * The reason code for provisioning failure when a {@link PasspointConfiguration} is failed to
+     * install.
+     */
+    public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22;
+
+    /**
      * The status code for provisioning flow to indicate connecting to OSU AP
      */
     public static final int OSU_STATUS_AP_CONNECTING = 1;
@@ -158,6 +200,12 @@
     public static final int OSU_STATUS_THIRD_SOAP_EXCHANGE = 10;
 
     /**
+     * The status code for provisioning flow to indicate starting a step retrieving trust root
+     * certs.
+     */
+    public static final int OSU_STATUS_RETRIEVING_TRUST_ROOT_CERTS = 11;
+
+    /**
      * Provisioning status for OSU failure
      *
      * @param status indicates error condition
@@ -170,5 +218,10 @@
      * @param status indicates status of OSU flow
      */
     public abstract void onProvisioningStatus(int status);
+
+    /**
+     * Provisioning complete when provisioning/remediation flow completes
+     */
+    public abstract void onProvisioningComplete();
 }